#2020学习打卡##C程序设计语言# C语言中的随机数函数解析

zkbhj 发表了文章 • 0 个评论 • 218 次浏览 • 2020-05-25 12:02 • 来自相关话题

在计算机中并没有一个真正的随机数发生器,但是可以做到使产生的数字重复率很低,这样看起来好象是真正的随机数,实现这一功能的程序叫伪随机数发生器。
 
有关如何产生随机数的理论有许多,如果要详细地讨论,需要厚厚的一本书的篇幅。不管用什么方法实现随机数发生器,都必须给它提供一个名为“种子”的初始值。而且这个值最好是随机的,或者至少这个值是伪随机的。“种子”的值通常是用快速计数寄存器或移位寄存器来生成的。
 在实际编程中,我们经常会用到随机数这个概念,其实也是一个伪随机数,实际上并不是一个真正的随机数,但是也足够我们使用了。在C语言中,编写一些关于游戏之类的程序时就需要用到随机数了。同时C语言也提供了一个标准库里面一个函数来产生随机数,而对于随机数的产生是根据种子(根据一个数值按照某种公式计算的)来变化的,种子 与随机数之间符合正态分布(高斯分布)。





 
生成随机数

在C语言中,我们一般使用 <stdlib.h> 头文件中的 rand() 函数来生成随机数,它的用法为:int rand (void);【void是指不需要传递参数】
rand() 会随机生成一个位于 0 ~ RAND_MAX 之间的整数。而对RAND_MAX 是 <stdlib.h> 头文件中的一个宏,它用来指明 rand() 所能返回的随机数的最大值。C语言标准并没有规定 RAND_MAX 的具体数值,只是规定它的值至少为 32767。/**
* 第35堂课示例:随机数
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>

int main()
{

int rands;

rands = rand();

printf("rand number is %d\n", rands);

printf("rand number2 is %d\n", rand());

return 0;
}但是这个随机数一旦编译之后就固定了,并不能满足我们的实际需求,前面提到了只是一个伪随机数,我们需要对产生随机数的种子进行不断的重播,从而达到我们实际需求的随机数效果。我们可以通过 srand() 函数来重新“播种”,这样种子就会发生改变。
 
srand() 的用法为:void srand (unsigned int seed);
它需要一个 unsigned int 类型的参数。在实际开发中,我们可以用时间作为参数,只要每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同,通常我们采用 <time.h> 头文件中的 time() 函数即可得到当前的时间【精准到秒】srand((unsigned)time(NULL));/**
* 第35堂课示例:随机数
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{

int rands;

srand((unsigned)time(NULL));

rands = rand();

printf("rand number is %d\n", rands);

printf("rand number2 is %d\n", rand());

return 0;
}小提示:根据种子与随机数的符合高斯分布的关系可知,生成的随机数是逐渐增大或者逐渐减小!
 
生成一定范围随机数

在实际编程开发中,实际需求往往是一定范围内的随机数,对于产生一定范围的随机数,就需要使用一定的技巧了,常用的方法是取模运算,再加上一个加法运算:int a = rand() % 10; //产生0~9的随机数,注意10会被整除
如果要规定上下限:int a = rand() % 51 + 100; //产生100~150的随机数
分析:取模即取余,rand()%51+13,看成两部分:rand()%51是产生 0~50 的随机数,后面+100保证 a 最小只能是 100,最大就是 50+100=150。/**
* 第35堂课示例:有区间的随机数
* 例如:100~150之间的数字
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{

int rands;

srand((unsigned)time(NULL));

rands = rand() % 51 + 100;

printf("rand number is %d\n", rands);

return 0;
}




 
根据种子与随机数的符合高斯分布的关系可知,生成的随机数是逐渐增大或者逐渐减小。 查看全部
在计算机中并没有一个真正的随机数发生器,但是可以做到使产生的数字重复率很低,这样看起来好象是真正的随机数,实现这一功能的程序叫伪随机数发生器
 
有关如何产生随机数的理论有许多,如果要详细地讨论,需要厚厚的一本书的篇幅。不管用什么方法实现随机数发生器,都必须给它提供一个名为“种子”的初始值。而且这个值最好是随机的,或者至少这个值是伪随机的。“种子”的值通常是用快速计数寄存器或移位寄存器来生成的。
 在实际编程中,我们经常会用到随机数这个概念,其实也是一个伪随机数,实际上并不是一个真正的随机数,但是也足够我们使用了。在C语言中,编写一些关于游戏之类的程序时就需要用到随机数了。同时C语言也提供了一个标准库里面一个函数来产生随机数,而对于随机数的产生是根据种子(根据一个数值按照某种公式计算的)来变化的,种子 与随机数之间符合正态分布(高斯分布)。

565EG.jpg

 
生成随机数

在C语言中,我们一般使用 <stdlib.h> 头文件中的 rand() 函数来生成随机数,它的用法为:
int rand (void);【void是指不需要传递参数】

rand() 会随机生成一个位于 0 ~ RAND_MAX 之间的整数。而对RAND_MAX 是 <stdlib.h> 头文件中的一个宏,它用来指明 rand() 所能返回的随机数的最大值。C语言标准并没有规定 RAND_MAX 的具体数值,只是规定它的值至少为 32767。
/**
* 第35堂课示例:随机数
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>

int main()
{

int rands;

rands = rand();

printf("rand number is %d\n", rands);

printf("rand number2 is %d\n", rand());

return 0;
}
但是这个随机数一旦编译之后就固定了,并不能满足我们的实际需求,前面提到了只是一个伪随机数,我们需要对产生随机数的种子进行不断的重播,从而达到我们实际需求的随机数效果。我们可以通过 srand() 函数来重新“播种”,这样种子就会发生改变。
 
srand() 的用法为:
void srand (unsigned int seed);

它需要一个 unsigned int 类型的参数。在实际开发中,我们可以用时间作为参数,只要每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同,通常我们采用 <time.h> 头文件中的 time() 函数即可得到当前的时间【精准到秒】srand((unsigned)time(NULL));
/**
* 第35堂课示例:随机数
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{

int rands;

srand((unsigned)time(NULL));

rands = rand();

printf("rand number is %d\n", rands);

printf("rand number2 is %d\n", rand());

return 0;
}
小提示:根据种子与随机数的符合高斯分布的关系可知,生成的随机数是逐渐增大或者逐渐减小!
 
生成一定范围随机数

在实际编程开发中,实际需求往往是一定范围内的随机数,对于产生一定范围的随机数,就需要使用一定的技巧了,常用的方法是取模运算,再加上一个加法运算:
int a = rand() % 10; //产生0~9的随机数,注意10会被整除

如果要规定上下限:
int a = rand() % 51 + 100; //产生100~150的随机数

分析:取模即取余,rand()%51+13,看成两部分:rand()%51是产生 0~50 的随机数,后面+100保证 a 最小只能是 100,最大就是 50+100=150。
/**
* 第35堂课示例:有区间的随机数
* 例如:100~150之间的数字
* 郑凯
* 2020年5月25日
* */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{

int rands;

srand((unsigned)time(NULL));

rands = rand() % 51 + 100;

printf("rand number is %d\n", rands);

return 0;
}

QQ截图20200525115709.jpg

 
根据种子与随机数的符合高斯分布的关系可知,生成的随机数是逐渐增大或者逐渐减小。

#2020学习打卡##C程序设计语言# C语言位字段的含义和用途

zkbhj 发表了文章 • 0 个评论 • 213 次浏览 • 2020-05-14 16:34 • 来自相关话题

存储空间很宝贵的情况下,可以考虑利用C语言位字段将多个数据保存在一个机器字。机器字指计算机一次能处理数据的bit位数,一般所说的32位系统即指其机器字长为32bit。
 
1.定义位字段:C语言位字段定义方法:
struct {
unsigned int a : 1; //冒号“:”后的数字为该位字段所占的bit位数
unsigned int b : 3;
unsigned int c : 5;
}flags;上面定义了一个flags变量,包含a,b,c三个位字段。位字段只能定义为以下3中类型:

                      1.int 
                      2.signed int
                      3.unsigned int 
 需要提醒的是,位字段的赋值要特别小心范围,如unsigned int型flags.b只占3个位数,范围在000-111即0-7,超出范围会出现不可预知错误。

   2.位字段访问:
                      1.位字段通过“.”号访问:flags.a  ,  flags.b等等。
                      2.位字段没有独立的地址,不能进行取址操作。
                      3.位字段没有独立的存储空间,不能进行sizeof()操作。

  3.内存分配规则:
                      1.位字段按声明顺序在机器字内存储
                      2.位字段不能跨越机器字存储,上一个机器字空间不足时,该位字段将全部存到下一个机器字
 

  4.无名字段与0字段:                    struct {
unsigned int a : 1;
unsigned int : 3; //无名字段,不可访问,仅起占位作用
unsigned int : 0; //0字段,下一个位字段在新机器字边界
unsigned int b : 7;
}flags;
  受0字段作用, 位字段flags.b将在下一个机器字边界开始存储。
  查看全部
存储空间很宝贵的情况下,可以考虑利用C语言位字段将多个数据保存在一个机器字。机器字指计算机一次能处理数据的bit位数,一般所说的32位系统即指其机器字长为32bit。
 
1.定义位字段:C语言位字段定义方法:
 struct  {
unsigned int a : 1; //冒号“:”后的数字为该位字段所占的bit位数
unsigned int b : 3;
unsigned int c : 5;
}flags;
上面定义了一个flags变量,包含a,b,c三个位字段。位字段只能定义为以下3中类型:

                      1.int 
                      2.signed int
                      3.unsigned int 
 需要提醒的是,位字段的赋值要特别小心范围,如unsigned int型flags.b只占3个位数,范围在000-111即0-7,超出范围会出现不可预知错误。

   2.位字段访问:
                      1.位字段通过“.”号访问:flags.a  ,  flags.b等等。
                      2.位字段没有独立的地址,不能进行取址操作。
                      3.位字段没有独立的存储空间,不能进行sizeof()操作。

  3.内存分配规则:
                      1.位字段按声明顺序在机器字内存储
                      2.位字段不能跨越机器字存储,上一个机器字空间不足时,该位字段将全部存到下一个机器字
 

  4.无名字段与0字段:                   
 struct  {
unsigned int a : 1;
unsigned int : 3; //无名字段,不可访问,仅起占位作用
unsigned int : 0; //0字段,下一个位字段在新机器字边界
unsigned int b : 7;
}flags;

  受0字段作用, 位字段flags.b将在下一个机器字边界开始存储。
 

数据结构之:二叉树

zkbhj 发表了文章 • 0 个评论 • 238 次浏览 • 2020-05-08 11:56 • 来自相关话题

在讨论二叉树之前,先复习几个重点的概念定义~

结点(Node)

结点是数据结构中的基础,是构成复杂数据结构的基本组成单位。 
树(Tree)
 
树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:
1)有且仅有一个特定的称为根(Root)的结点;
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、......、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。

此外,树的定义还需要强调以下两点:
1)n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2)m>0时,子树的个数没有限制,但它们一定是互不相交的。
 
结点的度
 
结点拥有的子树数目称为结点的度。
 
结点关系
 
结点子树的根结点为该结点的孩子结点。相应该结点称为孩子结点的双亲结点。同一个双亲结点的孩子结点之间互称兄弟结点。

结点层次
 
从根开始定义起,根为第一层,根的孩子为第二层,以此类推。
 
树的深度

树中结点的最大层次数称为树的深度或高度。
 
概念复习完成之后,进入二叉树正题!
 
二叉树
 
定义

二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
下图展示了一棵普通二叉树:






二叉树特点

由二叉树定义以及图示分析得出二叉树有以下特点:

1)每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
2)左子树和右子树是有顺序的,次序不能任意颠倒。
3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

二叉树性质

1)在二叉树的第i层上最多有2i-1 个节点 。(i>=1)
2)二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)
3)n0=n2+1 n0表示度数为0的节点数,n2表示度数为2的节点数。
4)在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整。
5)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:

(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。



作者:MrHorse1992
链接:https://www.jianshu.com/p/bf73c8d50dc2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 查看全部
在讨论二叉树之前,先复习几个重点的概念定义~

结点(Node)

结点是数据结构中的基础,是构成复杂数据结构的基本组成单位。 
树(Tree)
 
树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:
1)有且仅有一个特定的称为根(Root)的结点;
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、......、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。

此外,树的定义还需要强调以下两点:
1)n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2)m>0时,子树的个数没有限制,但它们一定是互不相交的。
 
结点的度
 
结点拥有的子树数目称为结点的度。
 
结点关系
 
结点子树的根结点为该结点的孩子结点。相应该结点称为孩子结点的双亲结点。同一个双亲结点的孩子结点之间互称兄弟结点。

结点层次
 
从根开始定义起,根为第一层,根的孩子为第二层,以此类推。
 
树的深度

树中结点的最大层次数称为树的深度或高度。
 
概念复习完成之后,进入二叉树正题!
 
二叉树
 
定义

二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
下图展示了一棵普通二叉树:

QQ截图20200508115253.jpg


二叉树特点

由二叉树定义以及图示分析得出二叉树有以下特点:

1)每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
2)左子树和右子树是有顺序的,次序不能任意颠倒。
3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

二叉树性质

1)在二叉树的第i层上最多有2i-1 个节点 。(i>=1)
2)二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)
3)n0=n2+1 n0表示度数为0的节点数,n2表示度数为2的节点数。
4)在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整。
5)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:


(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。




作者:MrHorse1992
链接:https://www.jianshu.com/p/bf73c8d50dc2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#2020学习打卡##C程序设计语言# void * 和 void **有什么区别?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 452 次浏览 • 2020-04-28 11:44 • 来自相关话题

#2020学习打卡##C程序设计语言# 如何把程序跟命令关联起来?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 476 次浏览 • 2020-04-26 17:27 • 来自相关话题

#2020学习打卡##C程序设计语言# C语言实现快速排序

zkbhj 发表了文章 • 0 个评论 • 235 次浏览 • 2020-04-23 17:24 • 来自相关话题

关于什么是快速排序,可以参考文章 :什么是快速排序。里面有介绍快速排序的方法以及和冒泡排序的区别,还有PHP版本实现快速排序的方法。本文章只讨论C语言实现的一种方式。#include <stdio.h>

//快速排序算法(从小到大)
//arr:需要排序的数组,begin:需要排序的区间左边界,end:需要排序的区间的右边界
void quickSort(int *arr,int begin,int end)
{
//如果区间不只一个数
if(begin < end)
{
int temp = arr[begin]; //将区间的第一个数作为基准数
int i = begin; //从左到右进行查找时的“指针”,指示当前左位置
int j = end; //从右到左进行查找时的“指针”,指示当前右位置
//不重复遍历
while(i < j)
{
//当右边的数大于基准数时,略过,继续向左查找
//不满足条件时跳出循环,此时的j对应的元素是小于基准元素的
while(i<j && arr[j] > temp)
j--;
//将右边小于等于基准元素的数填入右边相应位置
arr[i] = arr[j];
//当左边的数小于等于基准数时,略过,继续向右查找
//(重复的基准元素集合到左区间)
//不满足条件时跳出循环,此时的i对应的元素是大于等于基准元素的
while(i<j && arr[i] <= temp)
i++;
//将左边大于基准元素的数填入左边相应位置
arr[j] = arr[i];
}
//将基准元素填入相应位置
arr[i] = temp;
//此时的i即为基准元素的位置
//对基准元素的左边子区间进行相似的快速排序
quickSort(arr,begin,i-1);
//对基准元素的右边子区间进行相似的快速排序
quickSort(arr,i+1,end);
}
//如果区间只有一个数,则返回
else
return;
}
int main()
{
int num[12] = {23,45,17,11,13,89,72,26,3,17,11,13};
int n = 12;
quickSort(num,0,n-1);
printf("排序后的数组为:\n");
// for(int i=0;i<n;i++)
// printf("%d ", num[i]);
int *p = num;
while(n>0) {
n--;
printf("%d ",*p++);
}
return 0;
}
[/i][/i][/i][/i][/i][i][i][i][i]https://github.com/happy-hacki ... ort.c[/i][/i][/i][/i] 查看全部
关于什么是快速排序,可以参考文章 :什么是快速排序。里面有介绍快速排序的方法以及和冒泡排序的区别,还有PHP版本实现快速排序的方法。本文章只讨论C语言实现的一种方式。
#include <stdio.h>

//快速排序算法(从小到大)
//arr:需要排序的数组,begin:需要排序的区间左边界,end:需要排序的区间的右边界
void quickSort(int *arr,int begin,int end)
{
//如果区间不只一个数
if(begin < end)
{
int temp = arr[begin]; //将区间的第一个数作为基准数
int i = begin; //从左到右进行查找时的“指针”,指示当前左位置
int j = end; //从右到左进行查找时的“指针”,指示当前右位置
//不重复遍历
while(i < j)
{
//当右边的数大于基准数时,略过,继续向左查找
//不满足条件时跳出循环,此时的j对应的元素是小于基准元素的
while(i<j && arr[j] > temp)
j--;
//将右边小于等于基准元素的数填入右边相应位置
arr[i] = arr[j];
//当左边的数小于等于基准数时,略过,继续向右查找
//(重复的基准元素集合到左区间)
//不满足条件时跳出循环,此时的i对应的元素是大于等于基准元素的
while(i<j && arr[i] <= temp)
i++;
//将左边大于基准元素的数填入左边相应位置
arr[j] = arr[i];
}
//将基准元素填入相应位置
arr[i] = temp;
//此时的i即为基准元素的位置
//对基准元素的左边子区间进行相似的快速排序
quickSort(arr,begin,i-1);
//对基准元素的右边子区间进行相似的快速排序
quickSort(arr,i+1,end);
}
//如果区间只有一个数,则返回
else
return;
}
int main()
{
int num[12] = {23,45,17,11,13,89,72,26,3,17,11,13};
int n = 12;
quickSort(num,0,n-1);
printf("排序后的数组为:\n");
// for(int i=0;i<n;i++)
// printf("%d ", num[i]);
int *p = num;
while(n>0) {
n--;
printf("%d ",*p++);
}
return 0;
}
[/i][/i][/i][/i][/i]
[i][i][i][i]https://github.com/happy-hacki ... ort.c[/i][/i][/i][/i]

#2020学习打卡##C程序设计语言# 为什么在C语言里外部变量可以“重复定义”?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 321 次浏览 • 2020-04-23 16:58 • 来自相关话题

#2020学习打卡##C程序设计语言# C语言在Windows以及其他平台下如何用命令表达输入完成?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 326 次浏览 • 2020-04-21 11:22 • 来自相关话题

#2020学习打卡##C程序设计语言# 同样是字符串字面量赋值给指针变量*s和字符数组s[]有和区别?

zkbhj 发表了文章 • 0 个评论 • 225 次浏览 • 2020-04-20 10:07 • 来自相关话题

针对下面两种定义方式,会有什么不同?char s = "zkbhj";
char *s = "zkbhj"; 
不同如下:






在声明时,char *s=“hello”声明了一个字符串常量,在使用时不能被修改;

在声明时,char s=“hello”声明了一个字符串变量,在使用时能被修改;

作为函数的形式参数时,char *s,char s没有区别


例如:f(char *s)等价于 f(char s) 
 
参考文档:https://www.geeksforgeeks.org/ ... in-c/ 查看全部
针对下面两种定义方式,会有什么不同?
char s = "zkbhj";
char *s = "zkbhj";
 
不同如下:

CommonArticleDesign18-min.png


在声明时,char *s=“hello”声明了一个字符串常量,在使用时不能被修改;

在声明时,char s=“hello”声明了一个字符串变量,在使用时能被修改;

作为函数的形式参数时,char *s,char s没有区别



例如:f(char *s)等价于 f(char s) 
 
参考文档:https://www.geeksforgeeks.org/ ... in-c/

#2020学习打卡##C程序设计语言# C语言中static全局变量与普通的全局变量有什么区别?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 311 次浏览 • 2020-04-16 12:00 • 来自相关话题