第九次实验
(1)Python中的random模块提供了丰富的随机数相关函数,现要求用C语言仿照random模块实现一个函数库,其中应包括以下函数。
- random(): 产生一个[0, 1)的随机浮点数。
- uniform(a, b),用于生成一个指定范围内的随机浮点数,两个参数a和b:一个是上限,一个是下限。如果a>b,则生成的随机数n满足:b<=n<=a;如果a<b,则a<=n<=b。
- randint(a, b),用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,生成的随机数n满足:a<=n<=b。
- randrange(start, stop, step),从指定范围[start,stop]内按指定步长stop递增的集合中,获取一个随机数。例如,randrange(10, 100, 2),结果相当于从{10,12,14,16,…,96,98}集合中获取一个随机数。
要求:函数组织在两个文件myrandom.h和myrandom.c中,其中,myrandom.h包含上述函数的声明,myrandom.c包含上述函数的定义(实现);main()函数在文件source.c中;要对每个函数进行测试。这3个文件要放在一个工程中才能正确编译运行。
思路
利用C语言随机数生成函数完成
代码
// myrandom.h
#pragma once
double random();
double uniform(int a, int b);
int randint(int a, int b);
int randrange(int start, int stop, int step);
// myrandom.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "myrandom.h"
double random()
{
double r = (double)rand() / (RAND_MAX + 1.0);
return r;
}
double uniform(int a, int b)
{
double r = 0;
if (a < b)
r = random() * b + a;
else
r = random() * a + b;
return r;
}
int randint(int a, int b)
{
return (rand() % (b - a)) + a;
}
int randrange(int start, int stop, int step)
{
int arr[10001] = { 0 };
int len = 0;
while (len < (stop - start) / step)
{
arr[len] = start + step * len;
++len;
}
for (int i = 0; i < len; ++i)
printf("%d ", arr[i]);
int randLoc = rand() % len;
return arr[randLoc];
}
// main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "myrandom.h"
int main()
{
srand((unsigned int)time(NULL));
//printf("%lf",random());
//printf("%lf",uniform(1,10));
//printf("%d",randint(1,10));
//printf("%d",randrange(1,100,2));
return EXIT_SUCCESS;
}
(2)编写一个简单的猜数游戏,程序产生一个1-100的随机数,用户猜这个数,程序会给出“高了”或者“低了”的提示,直到用户猜对或者放弃(假定用户输入-1表示放弃)。如果用户最终猜对了,且猜的次数在5次以下,则输出“Good!”;猜的次数在5~8次,则输出“Not Bad!”;否则输出“You can do better!”。
思路
- 产生随机数;
- 输入猜测数,计数次数加一;
- 如果猜错,返回第2步;如果猜对,按要求输出并退出程序;如果放弃直接退出;
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int judge(int input, int randNum, int times)
{
int flag = 0;
if (input < randNum) printf("低了\n");
else if (input > randNum) printf("高了\n");
else
{
//printf("猜中了,共猜了%d次", times);
if (times < 5)
printf("Good\n");
else if (times < 8)
printf("Not Bad\n");
else
printf("You can do better\n");
flag = 1;
}
return flag;
}
void guessMain()
{
int randNum = rand() % 100;
int times = 0,
input = 0;
while (1)
{
printf("Please input a num(-1 for exit):\n");
scanf("%d", &input);
if (input == -1) break;
++times;
if (judge(input, randNum, times))
break;
}
}
int main()
{
guessMain();
return EXIT_SUCCESS;
}
(3)写一个递归程序,计算:
C
(
m
,
n
)
=
C
m
n
=
{
1
当
n
=
0
m
当
n
=
1
C
m
m
−
n
当
m
<
2
n
C
m
−
1
n
−
1
+
C
m
−
1
n
当
m
≥
2
n
C(m,n)=C_m^n= \begin{cases} 1&当n=0\\ m&当n=1\\ C_m^{m-n}&当m<2n\\ C_{m-1}^{n-1}+C_{m-1}^n&当m\ge2n \end{cases}
C(m,n)=Cmn=⎩⎪⎪⎪⎨⎪⎪⎪⎧1mCmm−nCm−1n−1+Cm−1n当n=0当n=1当m<2n当m≥2n
递归逻辑比较简单,直接贴代码
int recur(int m, int n)
{
if (n == 0) return 1;
if (n == 1) return m;
if (m < 2 * n) return recur(m, m - n);
else return recur(m - 1, n) + recur(m - 1, n - 1);
}
(4)写一个递归程序,计算一个算术表达式的值,其中只有+运算符。例如,5+3+9+1。要求首先描述问题的递归特征,即将问题分解为至少两部分,其中一部分可以直接解决,其他部分可以基于子问题的解来解决。
思路
此题只有加法运算,因此运算顺序不影响结果,本次采用从右往左运算进行递归。
递归特征描述为:
F
(
m
,
n
)
=
{
第
n
位
数
当
m
=
n
第
m
位
数
+
F
(
m
+
1
,
n
)
当
m
<
n
F(m,n)= \begin{cases} 第n位数&当m=n\\ 第m位数+F(m+1,n)&当m<n \end{cases}
F(m,n)={第n位数第m位数+F(m+1,n)当m=n当m<n
F(m,n)表示从第m位数到第n位数之间的表达式的值
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 100
void input(char exprs[], int &e_len, int n[], int &n_len)
{
printf("请输入加法表达式(=结尾):\n");
char ch;
// 处理一位数输入
int i = 0;
int j = 0;
while ((ch = getchar()) != '=')
{
if ('0' <= ch && ch <= '9')
n[i++] = ch - '0';
else
exprs[j++] = ch;
}
e_len = j;
n_len = i;
}
int compute(int a, char expr, int b)
{
int ans = 0;
switch (expr)
{
case '+':
ans = a + b;
break;
case '-':
ans = a - b;
break;
}
return ans;
}
int accumulate(int n[], int n_len, int pos)
{
if (n_len == pos + 1) return n[pos];
return compute(n[pos], '+', accumulate(n, n_len, pos + 1));
}
int main()
{
char exprs[N] = { '\0' };
int n[N] = { 0 };
int expr_len = 0,
n_len = 0;
input(exprs, expr_len, n, n_len);
int ans = accumulate(n, n_len, 0);
printf("%d", ans);
return EXIT_SUCCESS;
}
PS:本程序处理输入时未考虑多位数,因此只能进行一位数的加法运算,需要多位数加法还请自行实现多位数输入处理,算法逻辑无需修改。
(5)写一个递归程序,计算一个算术表达式的值,其中有+和-两种运算符。例如,5-3-9+1。同样,首先描述问题的递归特征。
思路
此题有加法减法两种运算,因此只能按照表达式运算顺序从左到右运算。
递归特征:
F
(
m
,
c
h
(
m
,
n
)
,
n
)
=
{
第
m
位
数
当
m
=
n
F
(
m
,
c
h
(
m
,
n
−
1
)
,
n
−
1
)
+
第
n
位
数
当
m
≤
n
F(m,ch(m,n),n)= \begin{cases} 第m位数&当m=n\\ F(m,ch(m,n-1),n-1)+第n位数&当m\le n \end{cases}
F(m,ch(m,n),n)={第m位数F(m,ch(m,n−1),n−1)+第n位数当m=n当m≤n
F(m,ch(m,n),n)表示从第m个数到第n个数的表达式的值,ch(m,n)表示之间的运算符。
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 100
void input(char exprs[], int* e_len, int n[], int* n_len)
{
printf("请输入加法表达式(=结尾):\n");
char ch;
// 处理一位数输入
int i = 0;
int j = 0;
while ((ch = getchar()) != '=')
{
if ('0' <= ch && ch <= '9')
n[i++] = ch - '0';
else
exprs[j++] = ch;
}
*e_len = j;
*n_len = i;
}
int compute(int a, char expr, int b)
{
int ans = 0;
switch (expr)
{
case '+':
ans = a + b;
break;
case '-':
ans = a - b;
break;
}
return ans;
}
int accumulate(int n[], int n_len, char exprs[])
{
if (0 == n_len-1) return n[n_len-1];
return compute(accumulate(n, n_len-1, exprs), exprs[n_len - 2], n[n_len - 1]);
}
int main()
{
char exprs[N] = { '\0' };
int n[N] = { 0 };
int expr_len = 0,
n_len = 0;
input(exprs, &expr_len, n, &n_len);
int ans = accumulate(n, n_len, exprs);
printf("%d", ans);
return EXIT_SUCCESS;
}
PS:本程序处理输入时未考虑多位数,因此只能进行一位数的加法运算,需要多位数加法还请自行实现多位数输入处理,算法逻辑无需修改。
(6)写一个递归程序,输出1~n个自然数中任意取k个数字的所有组合。程序运行时输入n和k的值。例如,如果输入n=5,k=3,则输出:123,124,125,134,135,145等组合。要求首先描述问题的递归特征。每个组合数可以以一个整数的形式输出,但顺序不重要,例如,123,132,231等被认为是同一个组合。
思路
同一个组合只输出一次。
递归特征:
全排列特征
F
(
m
,
n
,
k
)
=
F
(
m
,
n
,
1
)
,
F
(
m
,
n
,
k
−
1
)
F
(
m
,
n
,
1
)
和
F
(
m
+
1
,
n
,
k
−
1
)
不
存
在
同
样
的
数
F(m,n,k)=F(m,n,1),F(m,n,k-1)\\ F(m,n,1)和F(m+1,n,k-1)不存在同样的数
F(m,n,k)=F(m,n,1),F(m,n,k−1)F(m,n,1)和F(m+1,n,k−1)不存在同样的数
组合特征(本题)
F
(
m
,
n
,
k
)
=
m
,
F
(
m
+
1
,
n
,
k
−
1
)
s
.
t
.
m
≤
n
F(m,n,k)=m,F(m+1,n,k-1)\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space s.t.m\le n
F(m,n,k)=m,F(m+1,n,k−1) s.t.m≤n
F(m,n,k)表示从m到n之前取k位数的组合
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N 101
void recur(int arr[],int len, int m, int n, int k)
{
for (int i = m; i <= n+1; ++i)
{
if (k == 0)
{
//printf("当前%d个元素\t", len);
for (int j = 0; j < len; ++j)
printf("%d", arr[j]);
printf("\n");
break;
}
arr[len] = i;
recur(arr, len+1, i+1, n, k - 1);
}
}
int main()
{
//recur(1, 5, 3);
int arr[N] = { 0 },
n = 0,
k = 0;
//n = 5; k = 3;
scanf("%d,%d", &n, &k);
recur(arr, 0, 1, n, k);
return EXIT_SUCCESS;
}
结语
- 代码仅对思路作出实现,效率比较低下,代码质量较差,还请谅解。
- 若以上思路或程序有误,还请不吝赐教。
- 如果您有改进意见,欢迎指出。
- 程序的相关问题都欢迎交流。