1. 计算机人群中颇为流行的一个笑话是“我29岁,但我不告诉你这个数字的基数!”如果基数是16,这个人实际上是41岁。编写一个程序,接受一个年龄作为命令行参数,并在2~36的范围中计算那个字面值小于等于29的最小基数。例如,如果用户输入41,程序应该计算出这个最小基数为16。因为在十六进制中,十进制41的值是29。
#include
#include
int main( int argc, char **argv ){
int result_value;
int initial_value;
int result_base;
int base;
int flag;
result_value = atoi( *++argv );
printf( "result_value = %dn", result_value );
initial_value = 29;
while( initial_value >= 10 ){
int dec = initial_value / 10;
int unit = initial_value % 10;
flag = 0;
base = 2;
while( base <= 36 ){
int compute_value = dec * base + unit;
if( compute_value > result_value ){
break;
} else if( compute_value == result_value ){
result_base = base;
flag = 1;
break;
}
++base;
}
if( flag == 1 ){
break;
}
--initial_value;
}
printf( "result_base = %dn", result_base );
return EXIT_SUCCESS;
}
2.编写一个函数,通过返回一个范围为1~6的随机整数来模拟掷骰子。注意,这6个值出现的概率应该相同。当这个函数第一次调用时,它应该用当天的当前时间作为种子来产生随机数。
解析:
除了“概率相等”这个要求,这个问题的其他部分非常简单。这里有个例子。普通情况下你将把一个随机数对6取模,产生一个0~5的值,将这个值加上1并返回。但是,如果随机数生成函数所返回的最大值是32767,那么这些值就不是“概率相等”。从0~32765返回的值所产生的0~5之间各个值的概率是相等。但是,最后两个值(32766和32767)的返回值将分别是0和1,这使它们的出现概率有所增加(是5462/32768而不是5461/32768)。由于我们需要的答案的范围很窄,因此这个差别是非常小的。如果这个函数试图产生一个范围在1和30000之间的随机数,那么前2768个值的出现概率将是后面那些值的两倍。程序中的循环用于消除这种错误,方法是一旦出现最后两个值,就产生另一个随机值。
*/
#include
#include
#include
#define MAX_OK_RAND (int)((((long)RAND_MAX + 1) / 6 ) * 6 - 1)
int throw_die( void );
int main( void ){
printf( "RAND_MAX = %d, MAX_OK_RAND = %dn", RAND_MAX, MAX_OK_RAND );
printf( "%d %d %d", throw_die(), throw_die(), throw_die() );
return EXIT_SUCCESS;
}
int throw_die( void ){
static int is_seeded = 0;
int value;
if( !is_seeded ){
is_seeded = 1;
srand( (unsigned int)time( NULL ) );
}
do{
value = rand();
} while( value > MAX_OK_RAND );
return value % 6 + 1;
}
3. 编写一个程序,以一种3岁小孩的方式来说明当前的时间(例如,时针在5上面,分在在12上面)。
#include
#include
#include
int main( void ){
time_t t;
struct tm *ptm;
t = time( NULL );
ptm = localtime( &t );
printf( "at present, hour hand locates on the %d digit, minute hand locates on the %d digit.n",
ptm->tm_hour, ptm->tm_min );
return EXIT_SUCCESS;
}
4. 编写一个程序,接受3个整数为命令行参数,把它们分别解释为月(1~12),日(1~31)和年(0~?)。然后,它应该打印这个日子是星期几(或将是星期几)。对于那个范围的年份,这个程序的结果才是正确的?
#include
#include
#include
int main( int argc, char **argv ){
const char *str[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
struct tm var_tm;
var_tm.tm_mon = atoi( *++argv );
var_tm.tm_mday = atoi( *++argv );
var_tm.tm_year = 1900 + atoi( *++argv );
printf( "var_tm.tm_mon = %02d, var_tm.tm_mday = %02dn"
"var_tm.tm_year = %4d, var_tm.tm_wday = %dn",
var_tm.tm_mon, var_tm.tm_mday, var_tm.tm_year, var_tm.tm_wday );
printf( "%sn", str[var_tm.tm_wday - 1] );
return EXIT_SUCCESS;
}
#include
#include
#include
double wind_chill( double temp, double velocity );
int main( void ){
double temp;
double velocity;
double result;
temp = -5.0;
velocity = 10.0;
result = wind_chill( temp, velocity );
printf( "temp = %.1f, velocity = %.1f, result = %.1fn", temp, velocity, result );
return EXIT_SUCCESS;
}
double wind_chill( double temp, double velocity ){
const double var_a = 10.45;
const double var_b = 10.0;
const double var_c = -1.0;
double var_x = 1.78816 ;
double var_t = 33.0 - temp;
double result;
result = 33.0 - (var_a + var_b * sqrt( velocity ) + var_c * velocity ) * var_t /
(var_a + var_b * sqrt( var_x ) + var_c * var_x );
return result;
}
6. 用于计算抵押的月付金额的公式如下所示:
P = AI / (1 - (1 + I)^(-N))
A是贷款的数量,I是每个时段的利率(小数形式,而不是百分比形式),N是贷款需要支付的时段。例如,一笔100000美元的20年期利率8%的贷款,每月需要支付836.44美元(20年共有240个支付时段,每个支付时段的利率为0.66667)。
double payment( double amount, double interest, int years );
year指定贷款的时期,amount是贷款的数量,interest是用百分比形式(例如12%)表示的利率。函数应该计算并返回贷款的月付金额。(四舍五入至美分)。
#include
#include
#include
double payment( double amount, double interest, int years );
int main( void ){
double amount;
double interest;
int years;
double pay;
amount = 100000.0;
years = 20;
interest = 0.08;
pay = payment( amount, interest, years );
printf( "amount = %.2lf, interest = %.5lf, years = %d, pay = %.2lfn",
amount, interest / 12, years, pay );
return EXIT_SUCCESS;
}
double payment( double amount, double interest, int years ){
return (amount * interest / 12) / (1 - pow( 1 + interest / 12, -(years * 12) ));
}
7.设计良好的随机数生成函数所生成的值看上去很像随机数,但随着时间的延长,其结果会显示出一致性。从随机值派生而来的数字也具有这些属性。例如,一个设计欠佳的随机数生成函数的返回值看上去像是随机数,但实际上却是奇数和偶数交替出现。如果将这些看似的随机数对2取模(例如,用于模拟硬币的结果),其结果将是一个0和1交叉的序列。另一种较差的随机数生成函数只返回奇数值,将这些值对2取模的结果将是一个连续的0序列。这两类值都无法作为随机值使用,因为它们不够“随机”。
编写一个程序,在你的系统中测试随机数生成函数。你应该生成10000个随机数并执行两种类型的测试。首先是频率测试,把每个随机数对2取模,看看结果0和1的次数各有多少。然后对3~10进行同样的测试。这些结果将不会具有精确地一致性,但各个余数在频率上的峰谷差异不应该太大。
其次是周期型频率测试,取每个随机数和它之前的那个随机数,将它们对2取模,使用这两个余数作为一个二维数组的下标并增加指定位置的值。对3~10重复进行上面的取模测试。同样,这些结果将不会具有很严格的规律,但应该具有近似的一致性。
修改你的程序,以便可以为随机数生成函数提供不同的种子,并对使用几个不同的种子所产生的随机值进行测试。你的随机数生成函数是不是足够优秀?
解析:
这个程序从本质上来说是一个一次性程序,这个不优雅的解决方案用于完成这个任务是绰绰有余的。
#include
#include
int frequency2[2];
int frequency3[3];
int frequency4[4];
int frequency5[5];
int frequency6[6];
int frequency7[7];
int frequency8[8];
int frequency9[9];
int frequency10[10];
int cycle2[2][2];
int cycle3[3][3];
int cycle4[4][4];
int cycle5[5][5];
int cycle6[6][6];
int cycle7[7][7];
int cycle8[8][8];
int cycle9[9][9];
int cycle10[10][10];
#define CHECK( number, f_table, c_table )
remainder = x % number;
f_table[remainder] += 1;
c_table[remainder][last_x % number] += 1
#define PRINT_F( number, f_table )
printf( "nFrequency of random numbers modulo %dnt", number );
for( i = 0; i < number; i += 1 )
printf( " %5d", f_table[i] );
printf( "n" );
#define PRINT_C( number, c_table )
printf("nCyclic frequency of random numbers modulo %dn", number );
for( i = 0; i < number; i += 1 ){
printf( "t" );
for( j = 0; j < number; j += 1 ){
printf( " %5d", c_table[i][j] );
}
printf( "n" );
}
int main( int ac, char **av ){
int i;
int j;
int x;
int last_x;
int remainder;
if( ac > 1 ){
srand( atoi( av[1] ) );
}
last_x = rand();
for( i = 0; i < 10000; i += 1 ){
x = rand();
CHECK( 2, frequency2, cycle2 );
CHECK( 3, frequency3, cycle3 );
CHECK( 4, frequency4, cycle4 );
CHECK( 5, frequency5, cycle5 );
CHECK( 6, frequency6, cycle6 );
CHECK( 7, frequency7, cycle7 );
CHECK( 8, frequency8, cycle8 );
CHECK( 9, frequency9, cycle9 );
CHECK( 10, frequency10, cycle10 );
last_x = x;
}
PRINT_F( 2, frequency2 );
PRINT_F( 3, frequency3 );
PRINT_F( 4, frequency4 );
PRINT_F( 5, frequency5 );
PRINT_F( 6, frequency6 );
PRINT_F( 7, frequency7 );
PRINT_F( 8, frequency8 );
PRINT_F( 9, frequency9 );
PRINT_F( 10, frequency10 );
PRINT_C( 2, cycle2 );
PRINT_C( 3, cycle3 );
PRINT_C( 4, cycle4 );
PRINT_C( 5, cycle5 );
PRINT_C( 6, cycle6 );
PRINT_C( 7, cycle7 );
PRINT_C( 8, cycle8 );
PRINT_C( 9, cycle9 );
PRINT_C( 10, cycle10 );
return EXIT_SUCCESS;
}
8. 某个文件包含了家庭成员的年龄。同一个家庭成员的年龄位于一行,中间由一个空格分隔。例如,下面的数据
45 42 22
36 35 7 3 1
22 20
描述了3个分别具有3个、5个和2个成员的家庭的年龄。
编写一个程序,计算用这种文件形式表示的每个家庭的平均年龄。它应该使用%5.2f格式打印平均年龄,后面跟一个冒号和输入数据。这个问题和前一章的编程练习类似,但它没有家庭成员的数量的限制!但是,可以假定每个输入行的长度不超过512字符。
#include
#include
#include
#define BUFFER_SIZE 512
void average( FILE *input );
int main( void ){
FILE *age_file;
char age_file_name[] = "age_db.txt";
age_file = fopen( age_file_name, "r" );
if( !age_file ){
printf( "fail to open age_db_txt file.n" );
exit( EXIT_FAILURE );
}
average( age_file );
fclose( age_file );
return EXIT_SUCCESS;
}
void average( FILE *input ){
char buffer[BUFFER_SIZE];
char temp[BUFFER_SIZE];
while( fgets( buffer, BUFFER_SIZE, input ) ){
strcpy( temp, buffer );
char *token;
int age;
float average;
int sum;
int counter;
average = 0.0;
sum = 0;
counter = 0;
for( token = strtok( buffer, " "); token != NULL; token = strtok( NULL, " ") ){
age = atoi( token );
sum += age;
++counter;
}
average = sum * 1.0 / counter;
printf( "%5.2f:%sn", average, temp );
}
}
9. 在一个有30名学生的班级里,两个学生的生日是同一天的概率有多大?如果一群人中有两个成员的生日是同一天的概率为50%,那么这个人群应该有多少人?
编写一个程序,回答这些问题。取30个随机数,并把它们对365取模,分别表示一年内的各天(忽略闰年)。然后对这些值进行检查,看看有没有相同的。重复这个测试10000次,对这个频率做一个估计。
为了回答第2个问题,对程序进行修改,使其把人数作为一个命令行参数,把当天的时间作为随机数生成函数的种子,数次运行这个程序,以获得这个概率较为精确的估计值。
#include
#include
#include
#define ARR_LEN 30
#define TEST_NUM 10000
int main( int argc, char **argv ){
int counter;
int array[ARR_LEN];
int *p;
int *p2;
int i;
int num = atoi( *++argv );
int *person;
person = (int *)malloc( sizeof(int) * num );
for( i = 0; i < TEST_NUM; ++i ){
srand( (unsigned int)time( NULL) );
p = person;
p2 = person + num;
while( p < p2 ){
*p++ = rand() % 365;
}
counter = 0;
for( p = person; p < p2; ++p ){
printf( "%3d ", *p );
++counter;
if( counter % 5 == 0 ){
printf( "n" );
counter = 0;
}
}
counter = 0;
for( p = person; p < p2 - 1; ++p ){
int *p3;
for( p3 = p + 1; p3 < p2; ++p3 ){
if( *p == *p3 ){
printf( "%d ", *p );
++counter;
}
}
}
printf( "n" );
printf( "counter = %dn", counter );
printf( "the probability of two persons have a birthday day together in %d persons is %f%%n",
ARR_LEN, counter * 100.0 / 30 );
}
free( person );
return EXIT_SUCCESS;
}
10.插入排序(insertion sort)就是把值逐个插入到一个数组中。第一个值存储于数据的起始位置,后续的每个值在数组中寻找合适的插入位置。如果需要的话,可以对数组中原有的值进行移动以留出空间,然后再插入该值。
编写一个名叫insertion_sort的函数执行这个任务。它的原型应该和qsort函数一样。
提示:考虑把数组的左边作为已排序的部分,右边作为未排序的部分。最初已排序部分为空。在函数插入每个值时,已排序部分和未排序部分的边界向右移动,以便插入。当所有的元素都插入完毕时,未排序部分为空,数组排序完毕。
#include
#include
int compare( void const *a, void const *b );
void insertion_sort( void *base, size_t n_elements, size_t el_size,
int (*compare)( void const *, void const * ) );
int main( void ){
int array[] = { 8, 10, -6, 4 };
size_t array_len = sizeof(array) / sizeof(*array);
size_t elem_size = sizeof(*array);
int i;
printf( "print original elements of array:n" );
for( i = 0; i < array_len; ++i ){
printf( "%d ", array[i] );
}
printf( "n" );
insertion_sort( array, array_len, elem_size, compare );
printf( "after insertion_sort( array, array_len, elem_size, compare ),n"
"print original elements of array:n" );
for( i = 0; i < array_len; ++i ){
printf( "%d ", array[i] );
}
return EXIT_SUCCESS;
}
int compare( void const *a, void const *b ){
return *(char const *)a - *(char const *)b;
}
void insertion_sort( void *base, size_t n_elements, size_t el_size,
int (*compare)( void const *a, void const *b ) ){
int j;
for( j = 1; j < n_elements; ++j ){
char temp[el_size];
int m;
for( m = 0; m < el_size; ++m ){
temp[m] = *((char *)base + j * el_size + m);
}
printf( "1n" );
int i;
i = j;
while( i > 0 ){
int k;
int flag = 0;
for( k = 0; k < el_size; ++k ){
if( compare( (char const *)base + (i - 1) * el_size + k, (char const *)base + i * el_size + k ) > 0 ){
printf( "2.1n" );
flag = 1;
break;
} else if( compare( (char const *)base + (i - 1) * el_size + k, (char const *)base + i * el_size + k ) < 0 ){
printf( "2.2n" );
flag = 0;
break;
}
}
if( flag == 1 ){
printf( "3.1n" );
int n;
for( n = 0; n < el_size; ++n ){
char temp2;
temp2 = *((char *)base + i * el_size + n);
*((char *)base + i * el_size + n) = *((char *)base + (i - 1) * el_size + n);
*((char *)base + (i - 1) * el_size + n) = temp2;
}
} else{
printf( "3.2n" );
break;
}
--i;
}
for( m = 0; m < el_size; ++m ){
*((char *)base + i * el_size + m) = temp[m];
}
printf( "n" );
}
}



