- #include <stdio.h> 
- #include <stdlib.h> 
- #include <time.h> 
- #include <assert.h> 
- #ifdef _WIN32 
- #define NOMINMAX 
- #include <windows.h> 
- #else 
- double GetTickCount() 
- { 
- 	return clock() * 1000.0 / CLOCKS_PER_SEC; 
- } 
- #endif 
- #include <vector> 
- #include <set> 
- #include <algorithm> 
-   
- #if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(for) && _MSC_VER <= 1200 
- #define for if(0) {} else for 
- #endif // _MSC_VER && !__MWERKS__ && !for && _MSC_VER <= 1200 
- // msvc 'for' scoping hack 
-   
- static int n_Rand(int n_max) 
- { 
- 	int n = int(rand() / (double(RAND_MAX) + 1) * (n_max + 1)); 
- 	assert(n >= 0 && n <= n_max); 
- 	// get a random number 
-   
- 	return n; 
- } 
-   
- static void Print(int i) 
- { 
- 	printf("%d ", i); 
- } 
-   
- struct Dec { 
- 	void operator ()(int &r_n) const 
- 	{ 
- 		-- r_n; 
- 	} 
- }; 
-   
- template <class CScalar> 
- struct TNeedle { 
- 	CScalar n; 
-   
- 	TNeedle(const CScalar &r_n) 
- 		:n(r_n) 
- 	{} 
- }; 
-   
- template <class CScalar> 
- class CCompareWithOffset { 
- protected: 
- 	typename std::vector<CScalar>::iterator m_p_begin_it; /**< @brief iterator pointing to the first element of the vector */ 
-   
- public: 
- 	CCompareWithOffset(typename std::vector<CScalar>::iterator p_begin_it) 
- 		:m_p_begin_it(p_begin_it) 
- 	{} 
-   
- 	inline bool operator ()(const CScalar &r_value, TNeedle<CScalar> n) const 
- 	{ 
- 		size_t n_index = &r_value - &*m_p_begin_it; 
- 		// calculate index in the array 
-   
- 		return r_value < n.n + n_index; 
- 	} 
-   
- 	inline bool operator ()(TNeedle<CScalar> n, const CScalar &r_value) const 
- 	{ 
- 		size_t n_index = &r_value - &*m_p_begin_it; 
- 		// calculate index in the array 
-   
- 		return n.n + n_index < r_value; 
- 	} 
- }; 
-   
- namespace std { 
-   
- template <class RanIt, class V> 
- void iota(RanIt b, RanIt e, V counter) 
- { 
- 	if(b >= e) 
- 		return; 
- 	for(; b != e; ++ b, ++ counter) 
- 		*b = counter; 
- } 
-   
- } 
- // inject iota for old tyme people 
-   
- static void RandomUniqueSequence(std::vector<int> &rand_num, const size_t n_number_num, const size_t n_item_num) 
- { 
- 	assert(n_number_num <= n_item_num); 
-   
- 	rand_num.clear(); // !! 
- #if 0 
- 	// b1: 20250.000 msec 
- 	// b2: 2296.000 msec 
- 	std::set<int> numbers; 
- 	while(numbers.size() < n_number_num) 
- 		numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here 
- 	// generate unique random numbers 
-   
- 	rand_num.resize(numbers.size()); 
- 	std::copy(numbers.begin(), numbers.end(), rand_num.begin()); 
- 	// copy the numbers from a set to a list 
- #elif 0 
- 	// b1: 519515.000 msec 
- 	// b2: 20312.000 msec 
- 	std::vector<int> all_numbers(n_item_num); 
- 	std::iota(all_numbers.begin(), all_numbers.end(), 0); 
- 	// generate all the numbers 
-   
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		assert(all_numbers.size() == n_item_num - i); 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		rand_num.push_back(all_numbers[n]); // put it in the output list 
- 		all_numbers.erase(all_numbers.begin() + n); // erase it from the input 
- 	} 
- 	// generate random numbers 
- #elif 0 
- 	// b1: 2391.000 msec | 3187.000 msec (the fastest) 
- 	// b2: 51484.000 msec | 3734.000 msec 
- 	// b3: 5000.000 msec (10), 1172.000 msec (20), 3906.000 msec (50), 6938.000 msec (75), 10984.000 msec (100) 
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		size_t n_where = i; 
- 		for(size_t j = 0; j < i; ++ j) { 
- 			if(n >= rand_num[j]) 
- 				++ n; 
- 			else { 
- 				n_where = j; 
- 				break; 
- 			} 
- 		} 
- 		// see where it should be inserted 
-   
- 		rand_num.insert(rand_num.begin() + n_where, 1, n); 
- 		// insert it in the list, maintain a sorted sequence 
- 	} 
- 	// tier 0 - naive algorithm 
- #elif 0 
- 	// b1: 2391.000 msec | 3312.000 msec (the fastest) 
- 	// b2: 51406.000 msec | 4218.000 msec 
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		size_t n_where = i; 
- 		for(size_t j = 0; j < i; ++ j) { 
- 			if(n + j < rand_num[j]) { 
- 				n_where = j; 
- 				break; 
- 			} 
- 		} 
- 		// see where it should be inserted 
-   
- 		rand_num.insert(rand_num.begin() + n_where, 1, n + n_where); 
- 		// insert it in the list, maintain a sorted sequence 
- 	} 
- 	// tier 1 - use comparison with offset instead of increment 
- #elif 0 
- 	// b1: 3047.000 msec | 3750.000 msec 
- 	// b2: 99375.000 msec | 5828.000 msec 
- 	//rand_num.reserve(n_number_num); // makes it slightly slower 
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		size_t n_where = i; 
- 		for(size_t j = 0; j < i; ++ j) { 
- 			if(n /*+ j*/ < rand_num[j] /*+ j*/) { 
- 				n_where = j; 
- 				break; 
- 			} 
- 		} 
- 		// see where it should be inserted 
-   
- 		rand_num.insert(rand_num.begin() + n_where, 1, n); // each number encoded as value + its index 
- 		// insert it in the list, maintain a sorted sequence 
-   
- 		for(size_t j = n_where + 1; j <= i; ++ j) 
- 			-- rand_num[j]; 
- 		// compensate for insertion in the middle 
- 	} 
- 	for(size_t i = 1; i < n_number_num; ++ i) 
- 		rand_num[i] += i; // convert value + its index to absolute values 
- 	// tier 2 - use offset encoding 
- #elif 0 
- 	// b1: 4078.000 msec | 3781.000 msec 
- 	// b2: 33812.000 msec | 3203.000 msec 
- 	rand_num.reserve(n_number_num); // don't want insertion invalidating the iterators 
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(), n); 
- 		// see where it should be inserted 
-   
- 		rand_num.insert(p_where_it, 1, n); // each number encoded as value + its index 
- 		assert(*p_where_it == n); // make sure the iterator was not invalidated (if it were, this would potentially trigger a different assertion in the iterator dereference, or crash the program depending on the STL implementation) 
- 		// insert it in the list, maintain a sorted sequence 
-   
- 		//struct Dec { void operator ()(int &r_n) const { -- r_n; } }; // above 
- 		std::for_each(++ p_where_it, rand_num.end(), Dec()); 
- 		// compensate for insertion in the middle 
- 	} 
- 	for(size_t i = 1; i < n_number_num; ++ i) 
- 		rand_num[i] += i; // convert value + its index to absolute values 
- 	// tier 3 - use binary search with offset encoding 
- #elif 1 
- 	// b1: 4063.000 msec | 3578.000 
- 	// b2: 12907.000 msec | 1703.000 msec (the fastest) 
- 	// b3: 5656.000 msec (10), 1344.000 msec (20), 4172.000 (50), 6969.000 msec (75), 10094.000 msec (100) 
- 	for(size_t i = 0; i < n_number_num; ++ i) { 
- 		int n = n_Rand(n_item_num - i - 1); 
- 		// get a random number 
-   
- 		std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(), 
- 			TNeedle<int>(n), CCompareWithOffset<int>(rand_num.begin())); 
- 		// see where it should be inserted 
-   
- 		rand_num.insert(p_where_it, 1, n + (p_where_it - rand_num.begin())); // msvc goes crazy without the parens 
- 		// insert it in the list, maintain a sorted sequence 
- 	} 
- 	// tier 4 - use binary search 
-   
- 	// this does binary search with the comparison "n + j < rand_num[j]" with non-constant needle, 
- 	// which can be rewritten to one with constant needle and a modified sequence "n < rand_num[j] - j". 
- 	// it is easily shown that since the lowest distance between two elements of the original 
- 	// sequence "rand_num[j]" is: 
- 	// 
- 	//		rand_num[j] - rand_num[j - 1] >= 1 
- 	// 
- 	// (the generated numbers are unique), and at the same time, the difference 
- 	// between different indices of "-j" is: 
- 	// 
- 	//		-j - -(j - 1) = -1 
- 	// 
- 	// The difference between elements of their sum "rand_num[j] - j" is: 
- 	// 
- 	//		(rand_num[j] - j) - (rand_num[j - 1] - (j - 1)) >= 0 
- 	// 
- 	// therefore, the sequence is nondecreasing and this comparison can be used. 
- #endif // 0 
- } 
-   
- int main(int n_arg_num, const char **p_arg_list) 
- { 
- 	srand(int(time(0))); 
-   
- 	const size_t n_number_num = 3; 
- 	const size_t n_item_num = 7; 
- 	std::vector<int> rand_num; 
-   
- 	std::vector<size_t> histogram(n_item_num); 
- 	for(size_t n_pass = 0; n_pass < 10000; ++ n_pass) { 
- 		RandomUniqueSequence(rand_num, n_number_num, n_item_num); 
-   
- 		assert(rand_num.size() == n_number_num); // make sure we generated all the numbers 
- 		for(size_t i = 0, n = rand_num.size(); i < n; ++ i) 
- 			assert(rand_num[i] >= 0 && size_t(rand_num[i]) < n_item_num); // make sure the numbers are in range 
- 		std::sort(rand_num.begin(), rand_num.end()); 
- 		assert(std::unique(rand_num.begin(), rand_num.end()) == rand_num.end()); // make sure the numbers are unique 
- 		// debug checks 
-   
- 		for(size_t i = 0, n = rand_num.size(); i < n; ++ i) 
- 			++ histogram[rand_num[i]]; 
- 		// accumulate histograms to see the distribution 
- 	} 
-   
- 	printf("histogram over many iterations:\n"); 
- 	std::for_each(histogram.begin(), histogram.end(), &Print); 
- 	printf("\n"); 
-   
- 	double t = GetTickCount() * 1e-3; 
-   
- 	/*for(size_t n_pass = 0; n_pass < 10000000; ++ n_pass) { 
- 		const size_t n_number_num = 7; 
- 		const size_t n_item_num = 5000; 
- 		RandomUniqueSequence(rand_num, n_number_num, n_item_num); 
- 	}*/ 
- 	// benchmark 1 
-   
- 	for(size_t n_pass = 0; n_pass < 10000; ++ n_pass) { 
- 		const size_t n_number_num = 700; 
- 		const size_t n_item_num = 5000; 
- 		RandomUniqueSequence(rand_num, n_number_num, n_item_num); 
- 	} 
- 	// benchmark 2 
-   
- 	/*for(size_t n_pass = 0; n_pass < 1000000; ++ n_pass) { 
- 		const size_t n_number_num = 75; 
- 		const size_t n_item_num = 1000; 
- 		RandomUniqueSequence(rand_num, n_number_num, n_item_num); 
- 	}*/ 
- 	// benchmark 3 
-   
- 	t = GetTickCount() * 1e-3 - t; 
-   
- 	printf("done. it took %.3f msec\n", t * 1000); 
-   
- 	return 0; 
- } 
-