Related
I have to use the recursive selection sort in order to order different arrays of integers.
These arrays are respectively formed by 100, 1000, 10000, 100000, 200000, 500000 items and can be formed by ordered numbers, partially ordered numbers, inverted ordered numbers and random numbers.
After that I have to calculate the time the algorithm took to order the array.
I have to use recursion, It's a homework.
I created a function that generates the array:
typedef enum {ORINATO, INVERS, PARZ_ORDINATO, RANDOM} Ordine;
int *generaArray(int dimensione, Ordine ordine) {
int i, j, n;
int *array = (int*)malloc(dimensione * sizeof(int));
if (!array){
return NULL;
}
switch (ordine){
case ORINATO:
for (i = 0; i < dimensione; i++){
array[i] = i;
} break;
case INVERS:
n =0;
for ( i = dimensione-1; i >= 0 ; i--) {
array[i] = n;
n++;
}break;
case PARZ_ORDINATO:
for (i = 0; i < dimensione/2 ; i++) {
array[i] = i;
}
for (j = i+1; j <dimensione; j++){
n = rand();
array[j] = n;
};break;
case RANDOM:
for ( i = 0; i <= dimensione ; i++) {
array[i] = rand();
}break;
default:
break;
}
return array;
}
And it works like wonders.
Then I have created the recursive selection sort like follows:
void recursiveSelectionSort(int *array, int dim, int start){
int min=0;
if (start >= dim-1){
return;
}
min = findMin(array, start, start+1, dim);
swap(&array[min], &array[start]);
recursiveSelectionSort(array, dim, start+1);
}
int findMin(int *array, int min, int start, int dim){
if(start == dim ){
return min;
}
if (array[start]< array[min]){
min = start;
}
return findMin(array, min, start+1, dim);
}
void swap (int* x, int *y){
int temp = *x;
x = *y;
y = *temp;
}
Now, this as well should work but something clearly isn't. Let's make an example with the implementation, this is what i put in my main:
int main() {
int *array;
clock_t start, end;
double t;
array = generaArray(1000, ORINATO);
start = clock();
recursiveSelectionSort(array, 1000, 0);
end = clock();
t = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("\nIl tempo impiegato per 1000 elementi è: %lf secondi", t);
return 0;
}
This works (but it's slower thank it should be). However if you try and change the dimension from 1000 to 200000 or 500000 it shows error 11.
What is it causing it? I tried everything but it doesn't seem to work.
For starters recursive functions called for large arrays can invoke a stack overflow.
So use non-recursive functions that implement the method selection sort for large arrays.
As for your implementation then for example the function swap has typos.
void swap (int* x, int *y){
int temp = *x;
x = *y;
y = *temp;
}
I think you mean
void swap (int* x, int *y){
int temp = *x;
*x = *y;
*y = temp;
}
All other functions have too many parameters.
For example the function findMin can be declared the following way
size_t findMin( const int *a, size_t n );
and can be also defined as a recursive function (if you decided to write recursive functions then this function can be also recursive)
Here is a demonstrative program that shows how the functions can be defined
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap( int *x, int *y )
{
int temp = *x;
*x = *y;
*y = temp;
}
size_t findMin( const int a[], size_t n )
{
if ( n < 2 )
{
return 0;
}
else
{
size_t i = findMin( a + 1, n - 1 ) + 1;
return a[i] < a[0] ? i : 0;
}
}
void recursiveSelectionSort( int a[], size_t n )
{
if ( !( n < 2 ) )
{
size_t i = findMin( a + 1, n - 1 ) + 1;
if ( a[i] < a[0] ) swap( &a[0], &a[i] );
recursiveSelectionSort( a + 1, n - 1 );
}
}
int main(void)
{
enum { N = 15 };
int a[N];
srand( ( unsigned int )time( NULL ) );
for ( size_t i = 0; i < N; i++ )
{
a[i] = rand() % N;
}
for ( size_t i = 0; i < N; i++ )
{
printf( "%d ", a[i] );
}
putchar( '\n' );
recursiveSelectionSort( a, N );
for ( size_t i = 0; i < N; i++ )
{
printf( "%d ", a[i] );
}
putchar( '\n' );
return 0;
}
The program output might look like
11 9 3 5 6 8 2 4 5 3 7 9 2 0 14
0 2 2 3 3 4 5 5 6 7 8 9 9 11 14
I'm looking for a function in ANSI C that would randomize an array just like PHP's shuffle() does. Is there such a function or do I have to write it on my own? And if I have to write it on my own, what's the best/most performant way to do it?
My ideas so far:
Iterate through the array for, say, 100 times and exchange a random index with another random index
Create a new array and fill it with random indices from the first one checking each time if the index is already taken (performance = 0 complexity = serious)
Pasted from Asmodiel's link to Ben Pfaff's Writings, for persistence:
#include <stdlib.h>
/* Arrange the N elements of ARRAY in random order.
Only effective if N is much smaller than RAND_MAX;
if this may not be the case, use a better random
number generator. */
void shuffle(int *array, size_t n)
{
if (n > 1)
{
size_t i;
for (i = 0; i < n - 1; i++)
{
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
EDIT: And here's a generic version that works for any type (int, struct, ...) through memcpy. With an example program to run, it requires VLAs, not every compiler supports this so you might want to change that to malloc (which will perform badly) or a static buffer large enough to accommodate any type you throw at it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* compile and run with
* cc shuffle.c -o shuffle && ./shuffle */
#define NELEMS(x) (sizeof(x) / sizeof(x[0]))
/* arrange the N elements of ARRAY in random order.
* Only effective if N is much smaller than RAND_MAX;
* if this may not be the case, use a better random
* number generator. */
static void shuffle(void *array, size_t n, size_t size) {
char tmp[size];
char *arr = array;
size_t stride = size * sizeof(char);
if (n > 1) {
size_t i;
for (i = 0; i < n - 1; ++i) {
size_t rnd = (size_t) rand();
size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
memcpy(tmp, arr + j * stride, size);
memcpy(arr + j * stride, arr + i * stride, size);
memcpy(arr + i * stride, tmp, size);
}
}
}
#define print_type(count, stmt) \
do { \
printf("["); \
for (size_t i = 0; i < (count); ++i) { \
stmt; \
} \
printf("]\n"); \
} while (0)
struct cmplex {
int foo;
double bar;
};
int main() {
srand(time(NULL));
int intarr[] = { 1, -5, 7, 3, 20, 2 };
print_type(NELEMS(intarr), printf("%d,", intarr[i]));
shuffle(intarr, NELEMS(intarr), sizeof(intarr[0]));
print_type(NELEMS(intarr), printf("%d,", intarr[i]));
struct cmplex cmparr[] = {
{ 1, 3.14 },
{ 5, 7.12 },
{ 9, 8.94 },
{ 20, 1.84 }
};
print_type(NELEMS(intarr), printf("{%d %f},", cmparr[i].foo, cmparr[i].bar));
shuffle(cmparr, NELEMS(cmparr), sizeof(cmparr[0]));
print_type(NELEMS(intarr), printf("{%d %f},", cmparr[i].foo, cmparr[i].bar));
return 0;
}
The following code ensures that the array will be shuffled based on a random seed taken from the usec time. Also this implements the Fisher–Yates shuffle properly. I've tested the output of this function and it looks good (even expectation of any array element being the first element after shuffle. Also even expectation for being the last).
void shuffle(int *array, size_t n) {
struct timeval tv;
gettimeofday(&tv, NULL);
int usec = tv.tv_usec;
srand48(usec);
if (n > 1) {
size_t i;
for (i = n - 1; i > 0; i--) {
size_t j = (unsigned int) (drand48()*(i+1));
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
I’ll just echo Neil Butterworth’s answer, and point out some trouble with your first idea:
You suggested,
Iterate through the array for, say, 100 times and exchange a random index with another random index
Make this rigorous. I'll assume the existence of randn(int n), a wrapper around some RNG, producing numbers evenly distributed in [0, n-1], and swap(int a[], size_t i, size_t j),
void swap(int a[], size_t i, size_t j) {
int temp = a[i]; a[i] = a[j]; a[j] = temp;
}
which swaps a[i] and a[j].
Now let’s implement your suggestion:
void silly_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, randn(n), randn(n)); // swap two random elements
}
Notice that this is not any better than this simpler (but still wrong) version:
void bad_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, randn(n));
}
Well, what’s wrong? Consider how many permutations these functions give you: With n (or 2×_n_ for silly_shuffle) random selections in [0, n-1], the code will “fairly” select one of _n_² (or 2×_n_²) ways to shuffle the deck. The trouble is that there are n! = _n_×(n-1)×⋯×2×1 possible arrangements of the array, and neither _n_² nor 2×_n_² is a multiple of n!, proving that some permutations are more likely than others.
The Fisher-Yates shuffle is actually equivalent to your second suggestion, only with some optimizations that change (performance = 0, complexity = serious) to (performance = very good, complexity = pretty simple). (Actually, I’m not sure that a faster or simpler correct version exists.)
void fisher_yates_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, i+randn(n-1-i)); // swap element with random later element
}
ETA: See also this post on Coding Horror.
There isn't a function in the C standard to randomize an array.
Look at Knuth - he has algorithms for the job.
Or look at Bentley - Programming Pearls or More Programming Pearls.
Or look in almost any algorithms book.
Ensuring a fair shuffle (where every permutation of the original order is equally likely) is simple, but not trivial.
Here a solution that uses memcpy instead of assignment, so you can use it for array over arbitrary data. You need twice the memory of original array and the cost is linear O(n):
void main ()
{
int elesize = sizeof (int);
int i;
int r;
int src [20];
int tgt [20];
for (i = 0; i < 20; src [i] = i++);
srand ( (unsigned int) time (0) );
for (i = 20; i > 0; i --)
{
r = rand () % i;
memcpy (&tgt [20 - i], &src [r], elesize);
memcpy (&src [r], &src [i - 1], elesize);
}
for (i = 0; i < 20; printf ("%d ", tgt [i++] ) );
}
The function you are looking for is already present in the standard C library. Its name is qsort. Random sorting can be implemented as:
int rand_comparison(const void *a, const void *b)
{
(void)a; (void)b;
return rand() % 2 ? +1 : -1;
}
void shuffle(void *base, size_t nmemb, size_t size)
{
qsort(base, nmemb, size, rand_comparison);
}
The example:
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
srand(0); /* each permutation has its number here */
shuffle(arr, 10, sizeof(int));
...and the output is:
3, 4, 1, 0, 2, 7, 6, 9, 8, 5
Assuming you may want to just access an array randomly instead of actually shuffling it, you can use the degenerative case of a linear congruential pseudo-random number generator
X_n+1 = (a Xn+c) mod N
where a is coprime to N
generates a random cycle over all values 0:N
Naturally you could store this sequence in an empty array.
uint32_t gcd ( uint32_t a, uint32_t b )
{
if ( a==0 ) return b;
return gcd ( b%a, a );
}
uint32_t get_coprime(uint32_t r){
uint32_t min_val = r>>1;
for(int i =0;i<r*40;i++){
uint64_t sel = min_val + ( rand()%(r-min_val ));
if(gcd(sel,r)==1)
return sel;
}
return 0;
}
uint32_t next_val(uint32_t coprime, uint32_t cur, uint32_t N)
{
return (cur+coprime)%N;
}
// Example output Array A in random order
void shuffle(float * A, uint32_t N){
uint32_t coprime = get_coprime(N);
cur = rand()%N;
for(uint32_t i = 0;i<N;i++){
printf("%f\n",A[cur]);
cur = next_val(coprime, cur, N);
}
Just run the following code first and modify it for your needs:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define arr_size 10
// shuffle array
void shuffle(int *array, size_t n) {
if (n > 1) {
for (size_t i = 0; i < n - 1; i++) {
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
// display array elements
void display_array(int *array, size_t n){
for (int i = 0; i < n; i++)
printf("%d ", array[i]);
}
int main() {
srand(time(NULL)); // this line is necessary
int numbers[arr_size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
printf("Given array: ");
display_array(numbers, arr_size);
shuffle(numbers, arr_size);
printf("\nShuffled array: ");
display_array(numbers, arr_size);
return 0;
}
You would have something like:
You get different shuffled arrays every time you run the code:
The same answer like Nomadiq but the Random is kept simple.
The Random will be the same if you call the function one after another:
#include <stdlib.h>
#include <time.h>
void shuffle(int aArray[], int cnt){
int temp, randomNumber;
time_t t;
srand((unsigned)time(&t));
for (int i=cnt-1; i>0; i--) {
temp = aArray[i];
randomNumber = (rand() % (i+1));
aArray[i] = aArray[randomNumber];
aArray[randomNumber] = temp;
}
}
I saw the answers and I've discovered an easy way to do it
#include <stdio.h>
#include <conio.h>
#include <time.h>
int main(void){
int base[8] = {1,2,3,4,5,6,7,8}, shuffled[8] = {0,0,0,0,0,0,0,0};
int index, sorted, discart=0;
srand(time(NULL));
for(index = 0; index<8; index++){
discart = 0;
while(discart==0){
sorted = rand() % 8;
if (shuffled[sorted] == 0){
//This here is just for control of what is happening
printf("-------------\n");
printf("index: %i\n sorted: %i \n", index,sorted);
printf("-------------\n");
shuffled[sorted] = base[index];
discart= 1;
}
}
}
//This "for" is just to exibe the sequence of items inside your array
for(index=0;index<8; index++){
printf("\n----\n");
printf("%i", shuffled[index]);
}
return 0;
}
Notice that this method doesn't allow duplicated items.
And at the end you can use either numbers and letters, just replacing them into the string.
This function will shuffle array based on random seed:
void shuffle(int *arr, int size)
{
srand(time(NULL));
for (int i = size - 1; i > 0; i--)
{
int j = rand() % (i + 1);
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
In the code example, I have a function that takes as parameters a pointer to an int ordered_array and a pointer to int shuffled_array and a number representing the length of both arrays. It picks in each loop a random number from the ordered_array and inserts it into the shuffled array.
void shuffle_array(int *ordered_array, int *shuffled_array, int len){
int index;
for(int i = 0; i < len; i++){
index = (rand() % (len - i));
shuffled_array[i] = ordered_array[index];
ordered_array[index] = ordered_array[len-i];
}
}
I didn't see it among answers so I propose this solution if it can help anybody:
static inline void shuffle(size_t n, int arr[])
{
size_t rng;
size_t i;
int tmp[n];
int tmp2[n];
memcpy(tmp, arr, sizeof(int) * n);
bzero(tmp2, sizeof(int) * n);
srand(time(NULL));
i = 0;
while (i < n)
{
rng = rand() % (n - i);
while (tmp2[rng] == 1)
++rng;
tmp2[rng] = 1;
arr[i] = tmp[rng];
++i;
}
}
I know how to sort an array(i.e. bubble sort) but I don't have any idea how I can sort an array according to n-th term. Could you give me idea or example if there is? Thank you for all appreciated answer.
#edit: how can be the program sensed a number with zeros I mean for 1 program sense 0001 or 00001 .... ?
Example:
2 --> nth digit
4 45 62 1 900 105 --> inputs
Output:
001 004 105 900 045 065
void bubble_sort(int iarr[], int num) {
int i, j, k, temp;
printf("\nUnsorted Data:");
for (k = 0; k < num; k++) {
printf("%5d", iarr[k]);
}
for (i = 1; i < num; i++) {
for (j = 0; j < num - 1; j++) {
if (iarr[j] > iarr[j + 1]) {
temp = iarr[j];
iarr[j] = iarr[j + 1];
iarr[j + 1] = temp;
}
}
printf("\nAfter pass %d : ", i);
for (k = 0; k < num; k++) {
printf("%5d", iarr[k]);
}
}
}
The quick answer is that your comparison function needs to look at the n-th digit instead of the whole number.
So if your original comparison was something like:
if (a < b) // handle a before b case
elseif (b < a) // handle b before a case
you'll want to change it to be:
aDigit = getNthDigit(a, n);
bDigit = getNthDigit(b, n);
if (aDigit < bDigit) // handle a before b case
elseif (bDigit < aDigit) // handle b before a case
You'll also have to implement getNthDigit, which would involve integer division and modulus operators.
Take a look at qsort for what a generic sort function requires. For your specific question, look at the sort algorithm you want to implement (i.e. bubble sort), and replace comparisons of elements with a function call to an order function. Your compare function should then extract the second digit and compare those digits.
Based on your code, you should change if (iarr[j] > iarr[j + 1]) with if(comp_gt(iarr[j], iarr[j + 1])). And, I would implement comp_gt by
int comp_gt(int a, int b)
{
int a_second_digit = (a / 10) % 10;
int b_second_digit = (b / 10) % 10;
return (a_second_digit < b_second_digit);
}
It means that you sort the numbers based on their n-th digit.
In the example you have, you see that the bolded digits (the second digit in every number) are the ones who define the order of the output.
Here is an example on how you can solve it (I am tuning it right now, because the method it uses to find a digit is wrong):
#include <stdio.h>
#include <math.h>
void quickSort(int a[], int first, int last, int n_th);
int pivot(int a[], int first, int last, int n_th);
void swap(int* a, int* b);
int n_th_digit(int number, int n);
void print(int array[], const int N);
int main() {
int test[] = { 7, 9, 1, 3, 6, 5, 2, 4 };
int N = sizeof(test) / sizeof(int);
int n_th = 0; // digit(from the end) to sort by
printf("Size of test array : %d\n", N);
printf("Before sorting : \n");
print(test, N);
quickSort(test, 0, N - 1, n_th);
printf("After sorting : \n");
print(test, N);
return 0;
}
/**
* Quicksort.
* #param a The array to be sorted.
* #param first The start of the sequence to be sorted.
* #param last The end of the sequence to be sorted.
* #param n_th The digit to sort by
*/
void quickSort(int a[], int first, int last, int n_th) {
int pivotElement;
if (first < last) {
pivotElement = pivot(a, first, last, n_th);
quickSort(a, first, pivotElement - 1, n_th);
quickSort(a, pivotElement + 1, last, n_th);
}
}
/**
* Find and return the index of pivot element.
* #param a The array.
* #param first The start of the sequence.
* #param last The end of the sequence.
* #param n_th The digit to sort by
* For example the third digit of 137
* requires n_th to be 0.
*
*/
int pivot(int a[], int first, int last, int n_th) {
int i, p = first;
int pivotElement = a[first];
for (i = first + 1; i <= last; i++) {
if (n_th_digit(a[i], n_th) <= n_th_digit(pivotElement, n_th)) {
p++;
swap(&a[i], &a[p]);
}
}
swap(&a[p], &a[first]);
return p;
}
/**
* Swap the parameters.
* #param a The first parameter.
* #param a The second parameter.
*/
void swap(int* a, int* b) {
// You still can use the swap that
// does not uses an extra variable
// from the C++ implementation.
int temp = *a;
*a = *b;
*b = temp;
}
int n_th_digit(int number, int n) {
if (number < 0)
number *= -1;
return fmod((number / pow(10, n)), 10);
}
/**
* Print an array.
* #param a The array.
* #param N The size of the array.
*/
void print(int a[], const int N) {
int i;
for (i = 0; i < N; i++)
printf("array[%d] = %d\n", i, a[i]);
}
I got the how to find the n-th digit from here and the quicksort from here.
Replace
void bubble_sort(int iarr[], int num) {
....
if (iarr[j] > iarr[j + 1])
With
void bubble_sort(int iarr[], int num, int term) {
unsigned pow10 = upow10(term - 1);
....
if (compareu(iarr[j], iarr[j + 1], pow10) > 0)
// To calculate pow(10, x) quickly
static unsigned upow10(unsigned y) {
unsigned z = 1;
unsigned base = 10;
while (y) {
if (y & 1) {
z *= base;
}
y >>= 1;
base *= base;
}
return z;
}
int compareu(int a1, int a2, unsigned pow10) {
unsigned b1 = abs(a1);
unsigned b2 = abs(a2);
b1 = (b1 / pow10) % 10;
b2 = (b2 / pow10) % 10;
if (b1 > b2) return 1;
if (b1 < b2) return -1;
return (a1 > a2) - (a1 < a2);
}
[Edit] per OP's update
Q: how can be the program sensed a number with zeros I mean for 1 program sense 0001 or 00001?
A: That is part of the code that reads input which is not posted. If code needs to distinguish between "0001" and "00001", then the whole problem is one of strings and not integers. In that case save each element as a string and do compares from a textual point-of-view.
Yet I suspect that is not the true coding goal. Simply use arithmetical compares and not be concerned with differing leading zeros.
The printf() function is another matter. To print at least term digits with leading 0, use "%0*d".
term = 2; // or 6 or 9, etc.
// printf("%5d", iarr[k]);
printf("%0*d ", term, iarr[k]);
I'm looking for a function in ANSI C that would randomize an array just like PHP's shuffle() does. Is there such a function or do I have to write it on my own? And if I have to write it on my own, what's the best/most performant way to do it?
My ideas so far:
Iterate through the array for, say, 100 times and exchange a random index with another random index
Create a new array and fill it with random indices from the first one checking each time if the index is already taken (performance = 0 complexity = serious)
Pasted from Asmodiel's link to Ben Pfaff's Writings, for persistence:
#include <stdlib.h>
/* Arrange the N elements of ARRAY in random order.
Only effective if N is much smaller than RAND_MAX;
if this may not be the case, use a better random
number generator. */
void shuffle(int *array, size_t n)
{
if (n > 1)
{
size_t i;
for (i = 0; i < n - 1; i++)
{
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
EDIT: And here's a generic version that works for any type (int, struct, ...) through memcpy. With an example program to run, it requires VLAs, not every compiler supports this so you might want to change that to malloc (which will perform badly) or a static buffer large enough to accommodate any type you throw at it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* compile and run with
* cc shuffle.c -o shuffle && ./shuffle */
#define NELEMS(x) (sizeof(x) / sizeof(x[0]))
/* arrange the N elements of ARRAY in random order.
* Only effective if N is much smaller than RAND_MAX;
* if this may not be the case, use a better random
* number generator. */
static void shuffle(void *array, size_t n, size_t size) {
char tmp[size];
char *arr = array;
size_t stride = size * sizeof(char);
if (n > 1) {
size_t i;
for (i = 0; i < n - 1; ++i) {
size_t rnd = (size_t) rand();
size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
memcpy(tmp, arr + j * stride, size);
memcpy(arr + j * stride, arr + i * stride, size);
memcpy(arr + i * stride, tmp, size);
}
}
}
#define print_type(count, stmt) \
do { \
printf("["); \
for (size_t i = 0; i < (count); ++i) { \
stmt; \
} \
printf("]\n"); \
} while (0)
struct cmplex {
int foo;
double bar;
};
int main() {
srand(time(NULL));
int intarr[] = { 1, -5, 7, 3, 20, 2 };
print_type(NELEMS(intarr), printf("%d,", intarr[i]));
shuffle(intarr, NELEMS(intarr), sizeof(intarr[0]));
print_type(NELEMS(intarr), printf("%d,", intarr[i]));
struct cmplex cmparr[] = {
{ 1, 3.14 },
{ 5, 7.12 },
{ 9, 8.94 },
{ 20, 1.84 }
};
print_type(NELEMS(intarr), printf("{%d %f},", cmparr[i].foo, cmparr[i].bar));
shuffle(cmparr, NELEMS(cmparr), sizeof(cmparr[0]));
print_type(NELEMS(intarr), printf("{%d %f},", cmparr[i].foo, cmparr[i].bar));
return 0;
}
The following code ensures that the array will be shuffled based on a random seed taken from the usec time. Also this implements the Fisher–Yates shuffle properly. I've tested the output of this function and it looks good (even expectation of any array element being the first element after shuffle. Also even expectation for being the last).
void shuffle(int *array, size_t n) {
struct timeval tv;
gettimeofday(&tv, NULL);
int usec = tv.tv_usec;
srand48(usec);
if (n > 1) {
size_t i;
for (i = n - 1; i > 0; i--) {
size_t j = (unsigned int) (drand48()*(i+1));
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
I’ll just echo Neil Butterworth’s answer, and point out some trouble with your first idea:
You suggested,
Iterate through the array for, say, 100 times and exchange a random index with another random index
Make this rigorous. I'll assume the existence of randn(int n), a wrapper around some RNG, producing numbers evenly distributed in [0, n-1], and swap(int a[], size_t i, size_t j),
void swap(int a[], size_t i, size_t j) {
int temp = a[i]; a[i] = a[j]; a[j] = temp;
}
which swaps a[i] and a[j].
Now let’s implement your suggestion:
void silly_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, randn(n), randn(n)); // swap two random elements
}
Notice that this is not any better than this simpler (but still wrong) version:
void bad_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, randn(n));
}
Well, what’s wrong? Consider how many permutations these functions give you: With n (or 2×_n_ for silly_shuffle) random selections in [0, n-1], the code will “fairly” select one of _n_² (or 2×_n_²) ways to shuffle the deck. The trouble is that there are n! = _n_×(n-1)×⋯×2×1 possible arrangements of the array, and neither _n_² nor 2×_n_² is a multiple of n!, proving that some permutations are more likely than others.
The Fisher-Yates shuffle is actually equivalent to your second suggestion, only with some optimizations that change (performance = 0, complexity = serious) to (performance = very good, complexity = pretty simple). (Actually, I’m not sure that a faster or simpler correct version exists.)
void fisher_yates_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, i+randn(n-1-i)); // swap element with random later element
}
ETA: See also this post on Coding Horror.
There isn't a function in the C standard to randomize an array.
Look at Knuth - he has algorithms for the job.
Or look at Bentley - Programming Pearls or More Programming Pearls.
Or look in almost any algorithms book.
Ensuring a fair shuffle (where every permutation of the original order is equally likely) is simple, but not trivial.
Here a solution that uses memcpy instead of assignment, so you can use it for array over arbitrary data. You need twice the memory of original array and the cost is linear O(n):
void main ()
{
int elesize = sizeof (int);
int i;
int r;
int src [20];
int tgt [20];
for (i = 0; i < 20; src [i] = i++);
srand ( (unsigned int) time (0) );
for (i = 20; i > 0; i --)
{
r = rand () % i;
memcpy (&tgt [20 - i], &src [r], elesize);
memcpy (&src [r], &src [i - 1], elesize);
}
for (i = 0; i < 20; printf ("%d ", tgt [i++] ) );
}
The function you are looking for is already present in the standard C library. Its name is qsort. Random sorting can be implemented as:
int rand_comparison(const void *a, const void *b)
{
(void)a; (void)b;
return rand() % 2 ? +1 : -1;
}
void shuffle(void *base, size_t nmemb, size_t size)
{
qsort(base, nmemb, size, rand_comparison);
}
The example:
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
srand(0); /* each permutation has its number here */
shuffle(arr, 10, sizeof(int));
...and the output is:
3, 4, 1, 0, 2, 7, 6, 9, 8, 5
Assuming you may want to just access an array randomly instead of actually shuffling it, you can use the degenerative case of a linear congruential pseudo-random number generator
X_n+1 = (a Xn+c) mod N
where a is coprime to N
generates a random cycle over all values 0:N
Naturally you could store this sequence in an empty array.
uint32_t gcd ( uint32_t a, uint32_t b )
{
if ( a==0 ) return b;
return gcd ( b%a, a );
}
uint32_t get_coprime(uint32_t r){
uint32_t min_val = r>>1;
for(int i =0;i<r*40;i++){
uint64_t sel = min_val + ( rand()%(r-min_val ));
if(gcd(sel,r)==1)
return sel;
}
return 0;
}
uint32_t next_val(uint32_t coprime, uint32_t cur, uint32_t N)
{
return (cur+coprime)%N;
}
// Example output Array A in random order
void shuffle(float * A, uint32_t N){
uint32_t coprime = get_coprime(N);
cur = rand()%N;
for(uint32_t i = 0;i<N;i++){
printf("%f\n",A[cur]);
cur = next_val(coprime, cur, N);
}
Just run the following code first and modify it for your needs:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define arr_size 10
// shuffle array
void shuffle(int *array, size_t n) {
if (n > 1) {
for (size_t i = 0; i < n - 1; i++) {
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
// display array elements
void display_array(int *array, size_t n){
for (int i = 0; i < n; i++)
printf("%d ", array[i]);
}
int main() {
srand(time(NULL)); // this line is necessary
int numbers[arr_size] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
printf("Given array: ");
display_array(numbers, arr_size);
shuffle(numbers, arr_size);
printf("\nShuffled array: ");
display_array(numbers, arr_size);
return 0;
}
You would have something like:
You get different shuffled arrays every time you run the code:
The same answer like Nomadiq but the Random is kept simple.
The Random will be the same if you call the function one after another:
#include <stdlib.h>
#include <time.h>
void shuffle(int aArray[], int cnt){
int temp, randomNumber;
time_t t;
srand((unsigned)time(&t));
for (int i=cnt-1; i>0; i--) {
temp = aArray[i];
randomNumber = (rand() % (i+1));
aArray[i] = aArray[randomNumber];
aArray[randomNumber] = temp;
}
}
I saw the answers and I've discovered an easy way to do it
#include <stdio.h>
#include <conio.h>
#include <time.h>
int main(void){
int base[8] = {1,2,3,4,5,6,7,8}, shuffled[8] = {0,0,0,0,0,0,0,0};
int index, sorted, discart=0;
srand(time(NULL));
for(index = 0; index<8; index++){
discart = 0;
while(discart==0){
sorted = rand() % 8;
if (shuffled[sorted] == 0){
//This here is just for control of what is happening
printf("-------------\n");
printf("index: %i\n sorted: %i \n", index,sorted);
printf("-------------\n");
shuffled[sorted] = base[index];
discart= 1;
}
}
}
//This "for" is just to exibe the sequence of items inside your array
for(index=0;index<8; index++){
printf("\n----\n");
printf("%i", shuffled[index]);
}
return 0;
}
Notice that this method doesn't allow duplicated items.
And at the end you can use either numbers and letters, just replacing them into the string.
This function will shuffle array based on random seed:
void shuffle(int *arr, int size)
{
srand(time(NULL));
for (int i = size - 1; i > 0; i--)
{
int j = rand() % (i + 1);
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
In the code example, I have a function that takes as parameters a pointer to an int ordered_array and a pointer to int shuffled_array and a number representing the length of both arrays. It picks in each loop a random number from the ordered_array and inserts it into the shuffled array.
void shuffle_array(int *ordered_array, int *shuffled_array, int len){
int index;
for(int i = 0; i < len; i++){
index = (rand() % (len - i));
shuffled_array[i] = ordered_array[index];
ordered_array[index] = ordered_array[len-i];
}
}
I didn't see it among answers so I propose this solution if it can help anybody:
static inline void shuffle(size_t n, int arr[])
{
size_t rng;
size_t i;
int tmp[n];
int tmp2[n];
memcpy(tmp, arr, sizeof(int) * n);
bzero(tmp2, sizeof(int) * n);
srand(time(NULL));
i = 0;
while (i < n)
{
rng = rand() % (n - i);
while (tmp2[rng] == 1)
++rng;
tmp2[rng] = 1;
arr[i] = tmp[rng];
++i;
}
}
The task was to write heapsort for unknown type of elements in array (using only C code), but my code doesn't work. FOr the following numbers output is '-100 7 -4 0 33 -3 67 1 5 44' I also tried to use the same code for int input only and it worked perfectly. So I think the problem is in changing it to any type of input.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <memory.h>
typedef int typeEl;
int compare(const void* a, const void* b)
{
return (*(typeEl*)a - *(typeEl*)b);
}
void swap(void* a, void* b, size_t sizeOfElem) {
void* tmp = calloc(1,sizeOfElem);
memcpy(tmp, a, sizeOfElem);
memcpy(a, b, sizeOfElem);
memcpy(b, tmp, sizeOfElem);
}
void heapify (int pos, int n, void* arr, size_t sizeOfElem, int (*comp)(const void* c, const void* d)) {
while (2 * pos + 1 < n) {
int t = 2 * pos +1;
if (2 * pos + 2 < n && ((char *)arr + (2*pos+2)*sizeOfElem) >= ((char *)arr + t*sizeOfElem))
{
t = 2 * pos + 2;
}
if (comp((void *) ((char *)arr + pos*sizeOfElem), ((char *)arr + t*sizeOfElem))<0) {
swap((void *) ((char *)arr + pos*sizeOfElem), (void *) ((char *)arr + t*sizeOfElem), sizeOfElem);
pos = t;
}
else break;
}
}
void heap_make(int n, void* arr, size_t sizeOfElem)
{
for (int i = n - 1; i >= 0; i--)
{
heapify(i, n, arr, sizeOfElem, compare );
}
}
void heap_sort(int n, void* arr, size_t sizeOfElem)
{
heap_make(n, arr, sizeOfElem);
while(n>0)
{
swap((void *) ((char *)arr), (void *) ((char *)arr + (n-1)*sizeOfElem), sizeOfElem);;
n--;
heapify(0,n, arr, sizeOfElem, compare);
}
}
int main() {
int n;
int m[10] = {1,-3,5,-100,7,33,44,67,-4, 0};
heap_sort(10, m, sizeof(int));
for (n=0; n<10; n++)
printf ("%d ",m[n]);
return 0;
}
Anyone can advise? Thx for the help!
It can be very hard to decode someone else's code - especially when you are doing all kinds of complicated indexing etc. I had an old copy of a heapify lying around (from an earlier answer - https://stackoverflow.com/a/19749300/1967396 ) and I modified it to work for any type - and to include a full sort.
This doesn't completely answer the question "why is your code not working" - but it does show you some simple techniques you might want to implement as you try to answer the question. Typically, the more readable your code, the easier it is to debug.
Note that, in order to improve readability, I introduced a couple of extra variables (and additional lines of code) - childLeft and childRight are definitely useful; also, you can index elements once you have established a data type for their pointer - since you had a typedef at the start of your code, I simplified some things by doing
typeEl *array;
after which I could index the array with
array[pos];
which is much more readable (and thus less prone to errors) than
*((void *) ((char *)arr + pos*sizeOfElem))
I also defined swap in terms of the array and the indices of the two elements that needed swapping:
void swap(typeEl *arr, int i, int j)
which again makes the code considerably more readable (note also that I don't have to do a calloc).
Anyway - here is the code:
#include <stdio.h>
// define the type of the array that will be sorted:
typedef double typeEl;
void buildHeap(typeEl array[], int n);
void heapify(typeEl array[], int n, int i);
void heapsort(typeEl array[], int n);
void swap(typeEl array[], int indexA, int indexB);
void printTree(void);
int compare(typeEl a, typeEl b);
typeEl* TREE; // global variable used for printing the entire tree
int main(int argc, char *argv[])
{
typeEl heapArray[] = {1,-3,5,-100,7,33,44,67,-4, 0};
//{0, 1, 2, 3, 4, 5, 6 , 7, 8 ,9 ,10 , 11, 12, 13 ,14 ,15};
int n = sizeof(heapArray)/sizeof(typeEl);
TREE = heapArray;
printf("initial tree:\n");
printTree();
heapsort(heapArray, n);
printf("after heapify\n");
printTree();
printf("as a sorted array:\n");
for(int ii = 0; ii < n; ii++) printf("%d ", (int)heapArray[ii]);
printf("\n");
return 0;
}
void printTree(void)
{
// lazy way to print the entire tree...
typeEl* array = TREE;
printf(" %3d\n ", (int)array[0]);
printf(" %3d %3d\n", (int)array[1], (int)array[2]);
printf(" %3d %3d %3d %3d\n", (int)array[3], (int)(int)array[4], (int)array[5], (int)array[6]);
printf(" %3d %3d %3d\n", (int)array[7], (int)array[8], (int)array[9]);
printf("\n");
}
void heapsort(typeEl array[], int n) {
int i;
buildHeap(array, n);
for(i = 1; i < n; i++) {
buildHeap(array + i, n - i);
}
}
void buildHeap(typeEl array[], int n)
{
// change starting condition
int i = n/2;
while(i > 0) heapify(array, n,--i);
}
void heapify(typeEl array[], int n, int i)
{
// mark nodes initially as -1 to distinguish from "valid" zero
int childLeft = -1, childRight = -1;
int largest = i;
// see if we have valid child nodes:
if( 2 * i + 1 < n ) childLeft = 2 * i + 1;
if( 2 * i + 2 < n ) childRight = 2 * i + 2;
// see if any nodes are invalid now:
if(childLeft < 0 && childRight < 0) return;
if(childLeft < 0) childLeft = childRight; // can't happen, ever.
if(childRight < 0) childRight = childLeft;
// printf("child left [%i] = %i child right [%i] = %i\n", childLeft, array[childLeft], childRight, array[childRight]);
if (compare(array[childLeft], array[i] )) largest = childLeft;
if (compare(array[childRight] , array[largest])) largest = childRight;
if(largest != i)
{
swap(array, i,largest);
heapify(array, n, largest);
}
}
void swap(typeEl array[], int indexA, int indexB)
{
// printf("swap [%i] %i with [%i] %i\n", indexA, array[indexA], indexB, array[indexB]);
typeEl temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
int compare(typeEl a, typeEl b) {
return (a - b>0)?1:0;
}
Sample output:
initial tree:
1
-3 5
-100 7 33 44
67 -4 0
after heapify
67
44 33
7 5 1 0
-3 -4 -100
as a sorted array:
67 44 33 7 5 1 0 -3 -4 -100
As you can see it is sorted high - to - low. Obviously you can change that by simply changing the compare function.