I'm reading Accelerated C++. At the moment I'm at the end of chapter 3 and here's the exercise that I'm trying to do:
"Write a program to compute and print the quartiles of a set of integers."
I found the first and the second quartiles, but I have no idea how to find the third. Here's my code:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main(){
cout<<"Enter numbers:";
int x;
vector<int>integers;
while(cin>>x)
integers.push_back(x);
typedef vector<int>::size_type vec_sz;
vec_sz size = integers.size();
sort(integers.begin(), integers.end());
vec_sz mid = size/2;
vec_sz q1 = mid/2;
double median;
median = size % 2 == 0 ? ((double)integers[mid] + (double)integers[mid-1]) / 2
: integers[mid];
double quartOne = ((double)integers[q1] + (double)integers[q1-1])/2;
cout<<"The First Quartile is: "<<quartOne<<endl;
cout<<"The Second Quartile is: "<<median<<endl;
return 0;
}
One way would be to sort the collection and then take the 3 dividing items:
vector<int> v = ...;
sort(v.begin(), v.end());
int q12 = v[v.size()*1/4];
int q23 = v[v.size()*2/4];
int q34 = v[v.size()*3/4];
This is O(nlogn) in the number of data items.
Another way would be to perform a binary search of the data for the three divisions seperately. ie propose an initial q12, check if it is correct by making a pass of the data, if it is incorrect adjust it up or down by half, and repeat. Do likewise for q23 and q34.
This is technically O(n) because a 32-bit int has a fixed range and can be binary searched in 32 passes max.
This solutions implements the second method described in wikipedia for computing quartiles. It provides the correct values both for vectors with odd and even lengths.
#include <vector>
#include <tuple>
#include <iostream>
using namespace std;
double median(vector<double>::const_iterator begin,
vector<double>::const_iterator end) {
int len = end - begin;
auto it = begin + len / 2;
double m = *it;
if ((len % 2) == 0) m = (m + *(--it)) / 2;
return m;
}
tuple<double, double, double> quartiles(const vector<double>& v) {
auto it_second_half = v.cbegin() + v.size() / 2;
auto it_first_half = it_second_half;
if ((v.size() % 2) == 0) --it_first_half;
double q1 = median(v.begin(), it_first_half);
double q2 = median(v.begin(), v.end());
double q3 = median(it_second_half, v.end());
return make_tuple(q1, q2, q3);
}
int main() {
vector<double> v = {2, 2, 3, 4, 4, 5, 5, 10};
auto q = quartiles(v);
cout << get<0>(q) << "," << get<1>(q) << "," << get<2>(q) << endl;
return 0;
}
It is designed for real numbers, but it is easily adaptable for integer values (just round the values to their nearest integer).
Related
I'm new to CUDA and the thrust library. I'm learning and trying to implement a function that will have a for loop doing a thrust function. Is there a way to convert this loop into another thrust function? Or should I use a CUDA kernel to achieve this?
I have come up with code like this
// thrust functor
struct GreaterthanX
{
const float _x;
GreaterthanX(float x) : _x(x) {}
__host__ __device__ bool operator()(const float &a) const
{
return a > _x;
}
};
int main(void)
{
// fill a device_vector with
// 3 2 4 5
// 0 -2 3 1
// 9 8 7 6
int row = 3;
int col = 4;
thrust::device_vector<int> vec(row * col);
thrust::device_vector<int> count(row);
vec[0] = 3;
vec[1] = 2;
vec[2] = 4;
vec[3] = 5;
vec[4] = 0;
vec[5] = -2;
vec[6] = 3;
vec[7] = 1;
vec[8] = 9;
vec[9] = 8;
vec[10] = 7;
vec[11] = 6;
// Goal: For each row, count the number of elements greater than 2.
// And then find the row with the max count
// count the element greater than 2 in vec
for (int i = 0; i < row; i++)
{
count[i] = thrust::count_if(vec.begin(), vec.begin() + i * col, GreaterthanX(2));
}
thrust::device_vector<int>::iterator result = thrust::max_element(count.begin(), count.end());
int max_val = *result;
unsigned int position = result - count.begin();
printf("result = %d at position %d\r\n", max_val, position);
// result = 4 at position 2
return 0;
}
My goal is to find the row that has the most elements greater than 2. I'm struggling at how to do this without a loop. Any suggestions would be very appreciated. Thanks.
Solution using Thrust
Here is an implementation using thrust::reduce_by_key in conjunction with multiple "fancy iterators".
I also took the freedom to sprinkle in some const, auto and lambdas for elegance and readability. Due to the lambdas, you will need to use the -extended-lambda flag for nvcc.
#include <cassert>
#include <cstdio>
#include <thrust/reduce.h>
#include <thrust/device_vector.h>
#include <thrust/distance.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/discard_iterator.h>
#include <thrust/iterator/transform_iterator.h>
int main(void)
{
// fill a device_vector with
// 3 2 4 5
// 0 -2 3 1
// 9 8 7 6
int const row = 3;
int const col = 4;
thrust::device_vector<int> vec(row * col);
vec[0] = 3;
vec[1] = 2;
vec[2] = 4;
vec[3] = 5;
vec[4] = 0;
vec[5] = -2;
vec[6] = 3;
vec[7] = 1;
vec[8] = 9;
vec[9] = 8;
vec[10] = 7;
vec[11] = 6;
thrust::device_vector<int> count(row);
// Goal: For each row, count the number of elements greater than 2.
// And then find the row with the max count
// count the element greater than 2 in vec
// counting iterator avoids read from global memory, gives index into vec
auto keys_in_begin = thrust::make_counting_iterator(0);
auto keys_in_end = thrust::make_counting_iterator(row * col);
// transform vec on the fly
auto vals_in_begin = thrust::make_transform_iterator(
vec.cbegin(),
[] __host__ __device__ (int val) { return val > 2 ? 1 : 0; });
// discard to avoid write to global memory
auto keys_out_begin = thrust::make_discard_iterator();
auto vals_out_begin = count.begin();
// transform keys (indices) into row indices and then compare
// the divisions are one reason one might rather
// use MatX for higher dimensional data
auto binary_predicate = [col] __host__ __device__ (int i, int j){
return i / col == j / col;
};
// this function returns a new end for count
// b/c the final number of elements is often not known beforehand
auto new_ends = thrust::reduce_by_key(keys_in_begin, keys_in_end,
vals_in_begin,
keys_out_begin,
vals_out_begin,
binary_predicate);
// make sure that we didn't provide too small of an output vector
assert(thrust::get<1>(new_ends) == count.end());
auto const result = thrust::max_element(count.begin(), count.end());
int const max_val = *result;
auto const position = thrust::distance(count.begin(), result);
std::printf("result = %d at position %d\r\n", max_val, position);
// result = 4 at position 2
return 0;
}
Bonus solution using MatX
As mentioned in the comments NVIDIA has released a new high-level, C++17 library called MatX which targets problems involving (dense) multi-dimensional data (i.e. tensors). The library tries to unify multiple low-level libraries like CUFFT, CUSOLVER and CUTLASS in one python-/matlab-like interface. At the point of this writing (v0.2.2) the library is still in initial development and therefore probably doesn't guarantee a stable API. Due to this, the performance not being as optimized as with the more mature Thrust library and the documentation/samples not being quite exhaustive, MatX should not be used in production code yet. While constructing this solution I actually stumbled upon a bug which was instantly fixed. So this code will only work on the main branch and not with the current release v0.2.2 and some used features might not appear in the documentation yet.
A solution using MatX looks the following way:
#include <iostream>
#include <matx.h>
int main(void)
{
int const row = 3;
int const col = 4;
auto tensor = matx::make_tensor<int, 2>({row, col});
tensor.SetVals({{3, 2, 4, 5},
{0, -2, 3, 1},
{9, 8, 7, 6}});
// tensor.Print(0,0); // print full tensor
auto count = matx::make_tensor<int, 1>({row});
// count.Print(0); // print full count
// Goal: For each row, count the number of elements greater than 2.
// And then find the row with the max count
// the kind of reduction is determined through the shapes of tensor and count
matx::sum(count, matx::as_int(tensor > 2));
// A single value (scalar) is a tensor of rank 0:
auto result_idx = matx::make_tensor<matx::index_t>();
auto result = matx::make_tensor<int>();
matx::argmax(result, result_idx, count);
cudaDeviceSynchronize();
std::cout << "result = " << result()
<< " at position " << result_idx() << "\r\n";
// result = 4 at position 2
return 0;
}
As MatX employs deferred execution operators, matx::as_int(tensor > 2) is effectively fused into the kernel achieving the same as using a thrust::transform_iterator in Thrust.
Due to MatX knowing about the regularity of the problem while Thrust does not, the MatX solution could potentially be more performant than the Thrust solution. It certainly is more elegant. It is also possible to construct tensors in already allocated memory, so one can mix the libraries e.g. my constructing a tensor in the memory of a thrust::vector named vec via passing thrust::raw_pointer_cast(vec.data()) to the constructor of the tensor.
I have a range of base-10 logarithmically spaced points and I need to calculate the #points-per-decade for the points.
Based on this section from wikipedia we have #decades = log10(start / stop). From this we should be able to calculate #points-per-decade as #points / #decades. However this does not give the right answer. Here is a short program I've been using to test this method:
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <float.h>
class GenLog {
public:
GenLog(double start, double step) : curVal(start), step(step) {
m = 1.0 / step;
b = std::log10(start);
stepi = 0;
};
double operator()() {
++stepi;
double arg = m*stepi+b;
if (arg < DBL_MAX_10_EXP) {
curVal += pow(10.0, arg) - curVal;
} else {
curVal = DBL_MAX;
}
return curVal;
}
private:
double step, stepi, curVal, m, b;
};
int main(int argc, char *argv[])
{
if (argc < 5) {
std::cout << "Wrong number of args: format is [start] [stop] [points-per-decade] [size]\n";
return -1;
}
double start = atof(argv[1]);
double stop = atof(argv[2]);
double ppd = atof(argv[3]);
int size = atoi(argv[4]);
std::vector<double> vals;
vals.push_back(start);
// generate total number of points - 2 (excluding endpoints, GenLog takes in
// starting freq, and #point/dec
std::generate_n(std::back_inserter(vals), size - 2, GenLog(start, ppd));
vals.push_back(stop);
for (auto i : vals) {
std::cout << i << " ";
}
std::cout << "\n---TEST BACKWARDS PPD---\n";
std::cout << "ppd: " << ppd << "\t " << (vals.size()) / std::log10(*std::prev(vals.end()) / vals.front()) << "\n";
return 0;
}
Example output:
This generates a logarithmically spaced series of points from 1 to 10.1681 with 13 points per decade for a total of 15 points--although in principal you only need the starting point, and the points per decade to generate the next logarithmic point in the series.
As you can see the resulting numbers (13 and 14.8922) are not the same when they should be.
./so 1.0 10.1681 13 15
1 1.19378 1.4251 1.70125 2.03092 2.42446 2.89427 3.45511 4.12463 4.92388 5.87802 7.01704 8.37678 10 10.1681
---TEST BACKWARDS PPD---
ppd: 13 14.8922
based on my testing so far I do not think it is anything like an off-by-one error. Perhaps the calculations for #points-per-decade is conceptually incorrect? If so what is the correct way of calculating it?
My way of calculating #points-per-decade was incorrect
So based on the last equation in the calculations section of the wikipedia article we have:
step-size = 10^(1 / #points-per-decade)
since we know the step-size we can re-arrange to
1/#points-per-decade * ln(10)=ln(step-size)
and finally solve for #points-per-decade
#points-per-decade = ln(10) / ln(step-size)
I have a vector of digits, for example {3, 6, 0, 1, 8}
I need to covert it to an integer using every digit of a vector consistently.
So the number i'll get is 36018.
Possible solution:
std::vector<int> values = {1, 3, 4, 5};
int res = 0, s = values.size();
for(int num : values) res += num * pow(10, --s);
I want to know if there is some more "elegant", or short maybe, way to do this using stl algorithms.
You could use std::accumulate
std::vector<int> values = {1, 3, 4, 5};
int result = std::accumulate(values.begin(), values.end(), 0, [](int acc, int val){
return 10 * acc + val;
});
std::cout << result << std::endl; // 1345
A regular for loop is easier to read and therefore IMO is the more elegant choice:
int num = 0;
for (int d : values) {
num = num * 10 + d;
}
With C++20-Ranges or range-v3 it can be made quite readable
#include <iostream>
#include <vector>
#include <range/v3/all.hpp>
int main() {
std::vector<int> values{1, 2, 3};
auto powers_of_10 = ranges::view::generate([n = 1]() mutable {
auto res = n;
n *= 10;
return res;
});
auto num = ranges::inner_product(powers_of_10, values | ranges::view::reverse, 0);
std::cout << num << '\n';
}
The idea here is to produce a range of powers of 10 [1, 10, 100, ...] and then to simply calculate the dot product with the reversed input vector.
It could have been even more expressive if there were a iterate_view that iteratively applies a function to a value.
I have a program which searches for the largest and smallest number in an array of n elements in the C ++ language. What I want to do is to decrease the complexity of the algorithm a (3n / 2) - 2, which currently does not meet this complexity.
This complexity is in the worst case
My question is how can I leave this algorithm to the aforementioned complexity formula? Or what can I modify, delete and add to comply with that condition?
Thank you.
The comparison algorithm is as follows:
#include <iostream>
using namespace std;
int main(){
int arreglo[10] = {9,8,7,6,5,4,3,2,1,0};
int menor =0, mayor =0, comparaciones=0;
menor = arreglo[0], mayor = arreglo[0];
for(int i=1;i<10;i++){
if(arreglo[i]>mayor){
mayor = arreglo[i];
}
comparaciones++;
if(arreglo[i]<menor){
menor = arreglo[i];
}
comparaciones++;
}
cout<<"Mayor: "<<mayor<<" Menor: "<<menor<<" Comparaciones: "<<comparaciones;
}
UPDATE:
The algorithm has a complexity equation of 5n-2, I must lower its complexity to (3n / 2) - 2
This solution uses the Divide and Conquer paradigm.
I based this answer from this website and there you can see the explanation of why this will take (3n / 2) - 2 comparisons.
To understand how it works, I suggest getting a pen and paper and follow the code, using a smaller input (e.g.: {3,2,1,0}).
#include <iostream>
using namespace std;
int* maxMin(int* values, int begin, int end) {
int partialSmallest, partialLargest;
int mid, max1, min1, max2, min2;
//Here we store Largest/Smallest
int* result = new int[2];
//When there's only one element
if (begin == end) {
partialSmallest = values[begin];
partialLargest = values[begin];
}
else {
//There is not only one element, therefore
//We will split into two parts, and call the function recursively
mid = (begin + end) / 2;
// Solve both "sides"
int* result1 = maxMin(values, begin, mid);
int* result2 = maxMin(values, mid+1, end);
max1 = result1[0];
min1 = result1[1];
max2 = result2[0];
min2 = result2[1];
//Combine the solutions.
if (max1 < max2)
partialLargest = max2;
else
partialLargest = max1;
if (min1 < min2)
partialSmallest = min1;
else
partialSmallest = min2;
}
result[0] = partialLargest;
result[1] = partialSmallest;
return result;
}
int main(){
int values[10] = {9,8,7,6,5,4,3,2,1,0};
int* finalResult = maxMin(values, 0, 9);
cout << "Largest: " << finalResult[0] << " Smallest: " << finalResult[1];
}
I'm a programming student, and for a project I'm working on, on of the things I have to do is compute the median value of a vector of int values and must be done by passing it through functions. Also the vector is initially generated randomly using the C++ random generator mt19937 which i have already written down in my code.I'm to do this using the sort function and vector member functions such as .begin(), .end(), and .size().
I'm supposed to make sure I find the median value of the vector and then output it
And I'm Stuck, below I have included my attempt. So where am I going wrong? I would appreciate if you would be willing to give me some pointers or resources to get going in the right direction.
Code:
#include<iostream>
#include<vector>
#include<cstdlib>
#include<ctime>
#include<random>
#include<vector>
#include<cstdlib>
#include<ctime>
#include<random>
using namespace std;
double find_median(vector<double>);
double find_median(vector<double> len)
{
{
int i;
double temp;
int n=len.size();
int mid;
double median;
bool swap;
do
{
swap = false;
for (i = 0; i< len.size()-1; i++)
{
if (len[i] > len[i + 1])
{
temp = len[i];
len[i] = len[i + 1];
len[i + 1] = temp;
swap = true;
}
}
}
while (swap);
for (i=0; i<len.size(); i++)
{
if (len[i]>len[i+1])
{
temp=len[i];
len[i]=len[i+1];
len[i+1]=temp;
}
mid=len.size()/2;
if (mid%2==0)
{
median= len[i]+len[i+1];
}
else
{
median= (len[i]+0.5);
}
}
return median;
}
}
int main()
{
int n,i;
cout<<"Input the vector size: "<<endl;
cin>>n;
vector <double> foo(n);
mt19937 rand_generator;
rand_generator.seed(time(0));
uniform_real_distribution<double> rand_distribution(0,0.8);
cout<<"original vector: "<<" ";
for (i=0; i<n; i++)
{
double rand_num=rand_distribution(rand_generator);
foo[i]=rand_num;
cout<<foo[i]<<" ";
}
double median;
median=find_median(foo);
cout<<endl;
cout<<"The median of the vector is: "<<" ";
cout<<median<<endl;
}
The median is given by
const auto median_it = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it , len.end());
auto median = *median_it;
For even numbers (size of vector) you need to be a bit more precise. E.g., you can use
assert(!len.empty());
if (len.size() % 2 == 0) {
const auto median_it1 = len.begin() + len.size() / 2 - 1;
const auto median_it2 = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it1 , len.end());
const auto e1 = *median_it1;
std::nth_element(len.begin(), median_it2 , len.end());
const auto e2 = *median_it2;
return (e1 + e2) / 2;
} else {
const auto median_it = len.begin() + len.size() / 2;
std::nth_element(len.begin(), median_it , len.end());
return *median_it;
}
There are of course many different ways how we can get element e1. We could also use max or whatever we want. But this line is important because nth_element only places the nth element correctly, the remaining elements are ordered before or after this element, depending on whether they are larger or smaller. This range is unsorted.
This code is guaranteed to have linear complexity on average, i.e., O(N), therefore it is asymptotically better than sort, which is O(N log N).
Regarding your code:
for (i=0; i<len.size(); i++){
if (len[i]>len[i+1])
This will not work, as you access len[len.size()] in the last iteration which does not exist.
std::sort(len.begin(), len.end());
double median = len[len.size() / 2];
will do it. You might need to take the average of the middle two elements if size() is even, depending on your requirements:
0.5 * (len[len.size() / 2 - 1] + len[len.size() / 2]);
Instead of trying to do everything at once, you should start with simple test cases and work upwards:
#include<vector>
double find_median(std::vector<double> len);
// Return the number of failures - shell interprets 0 as 'success',
// which suits us perfectly.
int main()
{
return find_median({0, 1, 1, 2}) != 1;
}
This already fails with your code (even after fixing i to be an unsigned type), so you could start debugging (even 'dry' debugging, where you trace the code through on paper; that's probably enough here).
I do note that with a smaller test case, such as {0, 1, 2}, I get a crash rather than merely failing the test, so there's something that really needs to be fixed.
Let's replace the implementation with one based on overseas's answer:
#include <algorithm>
#include <limits>
#include <vector>
double find_median(std::vector<double> len)
{
if (len.size() < 1)
return std::numeric_limits<double>::signaling_NaN();
const auto alpha = len.begin();
const auto omega = len.end();
// Find the two middle positions (they will be the same if size is odd)
const auto i1 = alpha + (len.size()-1) / 2;
const auto i2 = alpha + len.size() / 2;
// Partial sort to place the correct elements at those indexes (it's okay to modify the vector,
// as we've been given a copy; otherwise, we could use std::partial_sort_copy to populate a
// temporary vector).
std::nth_element(alpha, i1, omega);
std::nth_element(i1, i2, omega);
return 0.5 * (*i1 + *i2);
}
Now, our test passes. We can write a helper method to allow us to create more tests:
#include <iostream>
bool test_median(const std::vector<double>& v, double expected)
{
auto actual = find_median(v);
if (abs(expected - actual) > 0.01) {
std::cerr << actual << " - expected " << expected << std::endl;
return true;
} else {
std::cout << actual << std::endl;
return false;
}
}
int main()
{
return test_median({0, 1, 1, 2}, 1)
+ test_median({5}, 5)
+ test_median({5, 5, 5, 0, 0, 0, 1, 2}, 1.5);
}
Once you have the simple test cases working, you can manage more complex ones. Only then is it time to create a large array of random values to see how well it scales:
#include <ctime>
#include <functional>
#include <random>
int main(int argc, char **argv)
{
std::vector<double> foo;
const int n = argc > 1 ? std::stoi(argv[1]) : 10;
foo.reserve(n);
std::mt19937 rand_generator(std::time(0));
std::uniform_real_distribution<double> rand_distribution(0,0.8);
std::generate_n(std::back_inserter(foo), n, std::bind(rand_distribution, rand_generator));
std::cout << "Vector:";
for (auto v: foo)
std::cout << ' ' << v;
std::cout << "\nMedian = " << find_median(foo) << std::endl;
}
(I've taken the number of elements as a command-line argument; that's more convenient in my build than reading it from cin). Notice that instead of allocating n doubles in the vector, we simply reserve capacity for them, but don't create any until needed.
For fun and kicks, we can now make find_median() generic. I'll leave that as an exercise; I suggest you start with:
typename<class Iterator>
auto find_median(Iterator alpha, Iterator omega)
{
using value_type = typename Iterator::value_type;
if (alpha == omega)
return std::numeric_limits<value_type>::signaling_NaN();
}