Ein Lernprogramm in C
main.c
/** @file main.c
@brief Choose one of several questions with a given probability. */
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* #define DEBUG 1 */
/** @def DEBUG_PRINT_DOUBLE( text, number )
@brief prints a text and a number in DEBUG mode. */
#ifdef DEBUG
#define DEBUG_PRINT_DOUBLE( text, number ) \
printf( text "%g\n", number );
#endif
#ifndef DEBUG
#define DEBUG_PRINT_DOUBLE( text, number )
#endif
/** @brief a propensity and a question-answer pair.
@details the propensity gives the relative frequency of
this entry (in relation to the sum of all propensities of
entries in a set). */
struct entry
{ double propensity;
char const * const question;
char const * const answer; };
/** @brief a list of entries with their propensities and texts. */
struct entry entry[]=
{ { 2.0, "meaning of 'elusive'",
"difficult to find, describe, remember, or achieve" },
{ 1.0, "meaning of 'reprimand'",
"speak to someone angrily or seriously for doing something" }};
/** @brief print the question of the component with the given
offset from the array »entry«.
@param i the offset of the component whose question is to be
printed */
void print_question( int const i )
{ puts( entry[ i ].question ); }
/** @brief print the answer of the component with the given
offset from the array »entry«.
@param i the offset of the component whose question is to be
printed */
void print_answer( int const i )
{ puts( entry[ i ].answer ); }
/** @brief the sum of all propensities.
@details this is set by calculate_the_sum_of_all_the_propensities
and used by calculate_the_summed_propensities. */
double sum;
/** @brief the type for a counter that wants to enumerate the
offset of all entries of the entry array. */
typedef size_t counter_t;
/** @brief the size of the entry array. */
counter_t const entry_length = sizeof entry / sizeof 0[ entry ];
/** @brief the entry at position p in this arrays contains the
sum of all probabilities given by the array entry up to p.
@details A probability is calculated from the propensity by
normalizing it so that the sum of a probabilities is 1.
@details This is set in calculate_the_summed_propensities and
used in print_the_next_random_entry. */
double summed[ ( int )( sizeof entry / sizeof 0[ entry ])];
/** @brief this calculates the sum of all the propensities
of the entry array and stores it in the variable sum. */
void calculate_the_sum_of_all_the_propensities( void )
{ sum = 0.0;
for( counter_t i = 0; i < entry_length; ++i )
sum += entry[ i ].propensity;
DEBUG_PRINT_DOUBLE( "sum = ", sum ); }
/** @brief this calculates the values in the array
summed so that a value at the position p contains the
sum of the probabilities of all the entries of the
array »entry« up to the position p. */
void calculate_the_summed_propensities( void )
{ double sumsofar = 0.0;
for( counter_t i = 0; i < entry_length; ++i )
{ double const probability = entry[ i ].propensity / sum;
sumsofar += probability;
summed[ i ]= sumsofar;
DEBUG_PRINT_DOUBLE( "sumsofar = ", sumsofar ); }
assert( fabs( sumsofar - 1.0 )< 1e-9 ); }
/** @brief returns a uniformly-distributed double value i,
with min <= i < top and with a number of rand() calls
that is smaller than a certain small number (like 4)
that depends on top-min and RAND_MAX.
@param min the minimum result
@param top the maximum result (exclusive)
@return a random number between min (inclusive)
and max (exclusive) */
double doublenumber( double min, double top )
{ { double step; do
{ step =( top - min )/( RAND_MAX + 1. );
min = min + rand() * step; top = min + step; }
while( min < top ); } return min; }
/** @brief returns a uniformly-distributed integral number i,
with 0 <= i < 1 and with a number of rand() calls
that is smaller than a certain small number (like 4)
that depends on top-min and RAND_MAX.
@return a random number between 0 (inclusive)
and 1 (exclusive) */
double random_number_between_0_and_1( void )
{ return doublenumber( 0, 1 ); }
/** @brief returns a random offset from the array »entry«.
@details when this function is called many times, each offset
of the array »entry« is returned with a frequency corresponding
to the probability of its propensity. */
counter_t next_random_offset( void )
{ double const number = random_number_between_0_and_1();
DEBUG_PRINT_DOUBLE( "number = ", number );
for( counter_t i = 0; i < entry_length; ++i )
if( summed[ i ] > number ){ return i; break; }
return entry_length > 1 ? number < 0.5 ? 0 :
entry_length - 1 : 0; }
/** @brief returns a random number from a measurement of some
random process.
@details The overall quality of randomness of this function is
not specified. In the worst case it might always return the same
number. */
unsigned int entropy( void )
{ return ( unsigned int )time( 0 )*( unsigned int )time( 0 )+
( unsigned int )time( 0 ); }
int main( void )
{
srand( entropy() * entropy() + entropy() );
rand(); rand(); rand();
assert( entry_length > 1 );
assert( entry_length < INT_MAX );
calculate_the_sum_of_all_the_propensities();
calculate_the_summed_propensities();
for( int i = 0; i < 10; ++i )
{ counter_t const o = next_random_offset();
print_question( o );
getchar();
print_answer( o );
puts( "----------------------------------------\n\n" ); }}