Ακέραιες Συναρτήσεις, Τυχαίοι Αριθμοί, Μετατροπή Συμβολοσειρών, Αναζήτηση και Ταξινόμηση <stdlib.h>


Για τη χρήση αυτών των συναρτήσεων πρέπει να γράψετε:

   #include <stdlib.h>

Αριθμητικές Συναρτήσεις

Υπάρχουν 2 βασικές ακέραιες συναρτήσεις:

int abs(int number);
div_t div(int numerator,int denominator);

με πολλές παραλαγές για διάφορα μεγέθη λέξης (8, 16, 32, 64 bits) και για int ή long int

abs()
επιστρέφει την απόλυτη τιμή του ορίσματος number. Π.χ. η abs(2) επιστρέφει 2 όπως και η abs(-2).
div()
δέχεται δύο ορίσματα, numerator και denominator και παράγει πηλίκο και υπόλοιπο ακέραιας διαίρεσης. Ο τύπος div_t ορίζεται (στη stdlib.h) ως εξής (ldiv_t για long int):
typedef struct {
int quot; /* quotient */
int rem; /* remainder */
} div_t;

Έτσι, το:

#include <stdlib.h>
....

int num = 8, den = 3;
div_t ans;

ans = div(num,den);

printf("Answer:\n\t Quotient = %d\n\t Remainder = %d\n", \
ans.quot,ans.rem);

παράγει:

Answer:
Quotient = 2
Remainder = 2

Τυχαίοι Αριθμοί

Οι τυχαίοι αριθμοί είναι χρήσιμοι σε προγράμματα προσομοίωσης τυχαίων γεγονότων, όπως σε παιγνίδια, προσομοιώσεις και πειραματισμούς. Στη πράξη καμμιά συνάρτηση δεν παράγει πραγματικά τυχαίους αριθμούς -- παράγονται ψευδο-τυχαίοι αριθμοί. Οι αριθμοί παράγονται με την εφαρμογή κάποιου μαθηματικού τύπου (διαφορετικού για κάθε γεννήτρια) και η ακολουθίες που παράγονται είναι επαναλήψιμες. Έτσι αν θέσετε τον ίδιο σπόρο (seed) λαμβάνετε την ίδια ακολουθία.

Μια κοινή τεχνική εισαγωγής περεταίρω τυχαιότητας είναι η χρήση της ώρας και της ημερομηνίας ως σπόρο, αφού αυτή συνεχώς μεταβάλλεςται. (Αργότερα θα δούμε πρότυπες συναρτήσεις χρόνου).

Στη πρότυπη βιβλιοθήκη υπάρχουν πολλές συναρτήσεις γέννησης (ψεύδο) τυχαίων αριθμών, για διαφορετικά διαστήματα αριθμών στο αποτέλεσμα. 

Μερικές απλές συναρτήσεις:

int rand(void);
void srand(unsigned int seed);
rand()
επιστρέφει διδοχικούς ψευδοτυχαίους ακέραιους αριθμούς στο διάστημα [0, (2**15)-1].
srand()
θέτει το σπόρο. Ένα απλό παράδειγμα κλήσης με χρήση της ώρας:
srand( (unsigned int) time( NULL ));

Το πρόγραμμα card.c που ακολουθεί δείχνει τη χρήση αυτών των συναρτήσεων στη προσομοίωση ανακατέματος τράπουλας:

/*
** Use random numbers to shuffle the "cards" in the deck. The second
** argument indicates the number of cards. The first time this
** function is called, srand is called to initialize the random
** number generator.
*/
#include <stdlib.h>
#include <time.h>
#define TRUE 1
#define FALSE 0

void shuffle( int *deck, int n_cards )
{
int i;
static int first_time = TRUE;

/*
** Seed the random number generator with the current time
** of day if we haven't done so yet.
*/
if( first_time ){
first_time = FALSE;
srand( (unsigned int)time( NULL ) );
}

/*
** "Shuffle" by interchanging random pairs of cards.
*/
for( i = n_cards - 1; i > 0; i -= 1 ){
int where;
int temp;

where = rand() % i;
temp = deck[ where ];
deck[ where ] = deck[ i ];
deck[ i ] = temp;
}
}

Υπάρχουν αρκετές άλλες γεννήτριες στη πρότυπη βιβλιοθήκη, για παράδειγμα:

double drand48(void);
double erand48(unsigned short xsubi[3]);
long lrand48(void);
long nrand48(unsigned short xsubi[3]);
long mrand48(void);
long jrand48(unsigned short xsubi[3]);
void srand48(long seed);
unsigned short *seed48(unsigned short seed[3]);
void lcong48(unsigned short param[7]);

Αυτή η οικογένεια δημιουργεί ομοιόμορφα κατανεμημένους τυχαίους αριθμούς σε διάφορα διαστήματα.

Οι drand48() και erand48() επιστρέφουν μη-αρνητικούς αριθμούς κινητής υποδιστολής με διπλή ακρίβεια ομοιόμορφα κατανεμημένους στο διάστημα [0.0, 1.0).

Οι lrand48() και nrand48() επιστρέφουν μη-αρνητικούς μακρούς ακεραίους  ομοιόμορφα κατανεμημένους στο διάστημα [0, 2**31).

Οι mrand48() και jrand48() επιστρέφουν προσημασμένους μακρούς ακεραίους  ομοιόμορφα κατανεμημένους στο διάστημα [-2**31, 2**31).

Οι srand48(), seed48()και lcong48() θέτουν σπόρους για τις drand48(), lrand48()ή mrand48() και μια από αυτές θα πρέπει πρώτα να έχει κληθεί.

Μετατροπή Συμβολοσειρών

Υπάρχουν αρκετές συναρτήσεις για τη μετατροπή συμβολοσειρών (string) σε ακεραίους, μακρούς ακεραίους ή αριθμούς κινητλης υποδιαστολής:

double atof(char *string) -- string σε floating point.
int atoi(char *string) -- string σε integer
int atol(char *string) -- string σε long integer.
double strtod(char *string, char *endptr) --  τμήμα string σε floating point.
long strtol(char *string, char *endptr, int radix) --  string σε long integer με συγκεκριμένη βάση (radix).
unsigned long strtoul(char *string, char *endptr, int radix) --  string σε unsigned long με συγκεκριμένη βάση. 

Μερικά παραδείγματα:

char *str1 = "100";
char *str2 = "55.444";
char *str3 = " 1234";
char *str4 = "123four";
char *str5 = "invalid123";

int i;
float f;

i = atoi(str1); /* i = 100 */
f = atof(str2); /* f = 55.44 */
i = atoi(str3); /* i = 1234 */
i = atoi(str4); /* i = 123 */
i = atoi(str5); /* i = 0 */

Σημείωση:

Αναζήτηση και Ταξινόμηση

  Η stdlib.h παρέχει αρκετές χρήσιμες συναρτήσεις για αναζήτηση και ταξινόμηση.  Περιλαμβάνουν το Quick Sort και σειριακές, δυαδικές και δενδρικές αναζητήσεις,  καθώς και συναρτήσεις Hashing.

Η συνάρτηση Quick Sort qsort() έχει σχεδιαστεί να ταξινομεί σε αύξουσας σειρά ένα πίνακα με βάση τη τιμή ενός κλειδιού οποιουδήποτε τύπου , αρκεί τα στοχεία του πίνακα να έχουν σταθερό τύπο:

void qsort(void *base, size_t num_elements, size_t element_size, comparison_fn_t compare); 
int comparison_fn_t (const void *, const void *);

Ένα παράδειγμα κλήσης:

...
double *array;
int size;
...

qsort (array, size, sizeof (double), compare_doubles);
...

όπου 

int compare_doubles (const void *a, const void *b)
{
const double *da = (const double *) a;
const double *db = (const double *) b;

return (*da > *db) - (*da < *db);
}

Παρόμοια υπάρχει η συνάρτηση Binary Search, bsearch():

void *bsearch(const void *key, const void *base, size_t nel,  size_t size, comparison_fn_t compare);
int comparison_fn_t (const void *, const void *);

Ένα παράδειγμα κλήσης. Έστω η δομή Record και η συνάρτηση σύγκρισης record_compare :

typedef struct {
int key;
struct other_data;
} Record;

int record_compare(void const *a, void const *a) {
return ( ((Record *)a)->key - ((Record *)b)->key );
}

Επίσης έστω οτι έχουμε ένα array από δομές Record μήκους array_length. Μπορούμε να καλέσουμε τη συνάρτηση bsearch() ως εξής:

Record key;
Record *ans;

key.key = 3; /* index value to be searched for */
ans = bsearch(&key, array, arraylength, sizeof(Record), record_compare);

Η συνάρτηση bsearch() επιστρέφει ένα δείκτη στο στοιχείου του πίνακα που το κλειδί του ταυτίστηκε με το αναζητούμενο, ή ένα NULL δείκτη αν δεν υπάρχει ταύτιση.

Σημειώστε οτι ο τύπος του ορίσματος key πρέπει να είναι ίδιος με αυτό των στοιχείων του πίνακς (δηλαδή Record), ακόμη και αν μόνο το πεδίο key.key χρειάζεται.

Ασκήσεις

Άσκηση 12534

Γράψτε ένα πρόγραμμα που να προσομοιώνει τη ρίψη ενός ζαριού έξι όψεων.

Άσκηση 12535

Γράψτε ένα πρόγραμμα που να προσομοιώνει το Lotto, με επιλογή έξι αριθμών στο διάστημα 1 - 49.

Άσκηση 12536

Γράψτε ένα πρόγραμμα που να διαβάζει έναν αριθμό κινητής υποδιαστολής από το πληκτρολόγιο και να παράγει ένα τυχαίο αριθμό κινητής υποδιαστολής στο διάστημα  0 - αριθμός εισόδου.

Άσκηση 12537

Μελετήστε τις υπόλοιπες αναζήτησης και ταξινόμησης και τροποποιείστε το παράδειγμα που δίνεται ώστε να τις χρησιμοποιήσετε.



Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
3/3/2008