左程云算法菜手班整理(二)

左程云算法菜手班整理(二)

左程云算法新手班

2.1实现前缀和数组

求前缀和可以用前缀和矩阵和前缀和数组,但是前缀和矩阵预处理过程代价较大,所以一般用前缀和数组,但是去相应的值需要进行减法处理。
前缀和数组通过预处理,可以在o(1)复杂度的情况下得到前i~j位的和,称为数组。

#include<iostream>
#include<vector>

using namespace std;

class NumArray
{
public:
	vector<int> sum;
	NumArray(vector<int>& nums)
	{
		int size = nums.size();
		sum.resize(size + 1);
		sum[0] = 0;
		for (int i = 0; i < size; i++)
		{
			sum[i + 1] = sum[i] + nums[i];
		}
	}
	int sumRange(int left, int right)
	{
		return sum[right + 1] - sum[left];
	}
};

2.2如何用1-5的随机函数加工出1~7的随机函数

思路:

  1. 要想返回 1-7,则直接让7减去1变为 0-6,然后0-6需要用三个二进制位才能表示(三位可以表示0-7,所以能包含0~6)
  2. 则只要制造一个函数,使其等概率返回0或者1,然后三个返回值拼凑到三个二进制位上,则就能保住同概率。 注意 0~7中的7不返回,继续调用此函数。
  3. 给你 1~5,则是 1 2 3 4 5 ,取中间值3不返回,1、2返回0;4,5返回1;写出此函数假设为 b函数
  4. 给定的函数假设为a函数,则思路1和思路2中的函数假设为c函数。
#include<iostream>
#include<ctime>
using namespace std;
//随机等概率生成1-5中的整数
int a()
{
	//要将随机数的范围限制在1和某个最大值max之间的整数,可以使用以下公式,rand()%max+1;
	//原理:如果想返回1-5的数,则对5取余,返回的就是0-4的数,然后再加1即可。
	//这个想法可以扩展到任意范围内的随机数,其通用公式如下:
	//number = (rand()%(maxValue-minValue+1))+minValue;

	return rand() % 5 + 1;
}
//利用给定的a(),等概率返回0和1
int b()
{
	int ret = 0;
	do {
		ret = a();
	} while (ret == 3);
	return ret < 3 ? 0 : 1;
}
int c()//0-6随机
{
	int ret = 0;
	do {
		ret = (b() << 2) + (b() << 1) + (b() << 0);//7,二进制表示111,用三位即可表示
	} while (ret == 7);
	return ret;
}

int main()
{
	unsigned seed;
	seed = time(0);
	srand(seed);//srand(seed)是随机数发生器的初始化函数,正确语法是srand(unsigned int seed);

	//用以验证是否随机生成了1-7的随机数。
	int test[8] = { 0 };
	for (int i = 0; i < 10000000; i++)
	{
		int res = c() + 1;//1-7随机
		test[res]++;
	}
	for (int i = 0; i < 8; i++)
	{
		cout << test[i] << ":" << endl;
	}
}

2.3如何用a-b的随机函数加工出c-d的随机函数

#include<iostream>
#include<ctime>
#include<vector>

using namespace std;

// 这个结构是唯一的随机机制
// 你只能初始化并使用,不可修改
class RandomBox
{
public:
	int min;
	int max;
	// 初始化时请一定不要让mi==ma
	RandomBox(int mi, int ma) {
		min = mi;
		max = ma;
	}
	// 13 ~ 17
	// 13 + [0,4]
	int random() {
		return min + (int)(rand() % (max - min + 1) + min);
	}
};
// 利用条件RandomBox,如何等概率返回0和1
int rand01(RandomBox randomBox) 
{
	int min = randomBox.min;
	int max = randomBox.max;
	// min ~ max
	int size = max - min + 1;
	// size是不是奇数,odd 奇数
	bool odd = (size & 1) != 0;//判断奇偶的小技巧,偶数和1位与一定为0;
	int mid = size / 2;
	int ans = 0;
	do {
		ans = randomBox.random() - min;
	} while (odd && ans == mid);//当为奇数并且结果是mid时返回。
	return ans < mid ? 0 : 1;
}

// 给你一个RandomBox,这是唯一能借助的随机机制
// 等概率返回from~to范围上任何一个数
// 要求from<=to
int random(RandomBox randomBox, int from, int to) {
	if (from == to) {
		return from;
	}
	// 3 ~ 9
	// 0 ~ 6
	// 0 ~ range
	int range = to - from;
	int num = 1;
	// 求0~range需要几个2进制位
	while ((1 << num) - 1 < range) {
		num++;
	}

	// 我们一共需要num位
	// 最终的累加和,首先+0位上是1还是0,1位上是1还是0,2位上是1还是0...
	int ans = 0;
	do {
		ans = 0;
		for (int i = 0; i < num; i++) {
			ans |= (rand01(randomBox) << i);//都是0和1结合,因此也可以用异或^,但是不能用与&。
		}
	} while (ans > range);
	return ans + from;
}
int main() {
	cout << "测试开始" << endl;
	// rand() -> double -> [0,1)
	int testTimes = 10000000;
	int count = 0;
	for (int i = 0; i < testTimes; i++) {
		if (rand() % 1 < 0.75) {
			count++;
		}
	}
	cout << ((double)count / (double)testTimes) << endl;

	cout << ("=========") << endl;

	// [0,1) -> [0,8)
	count = 0;
	for (int i = 0; i < testTimes; i++) {
		if (rand() % 8 < 5) {
			count++;
		}
	}
	cout << ((double)count / (double)testTimes) << endl;
	cout << ((double)5 / (double)8)<<endl;

	int K = 9;
	// [0,K) -> [0,8]
	vector<int> counts(9, -1);
	for (int i = 0; i < testTimes; i++) {
		int ans = (int)(rand() % K); // [0,K-1]
		counts[ans]++;
	}
	for (int i = 0; i < K; i++) {
		cout << i << "这个数,出现了 " <<counts[i] << "次" << endl;
	}
	cout << "=========" << endl;
}

2.4将不等概率函数转变为等概率函数

int f() 
{
	int a = rand() % 10;
	float b = a /10.0;
	return (b < 0.8) ? 1 : 0;
}//rand()返回的是int型,不能直接%1后转化为类似于java里面的math.random()那样的函数
    int g() {
        while(1)
        {
            int n1=RandomP::f();
            int n2=RandomP::f();
            if(n1==1&&n2==0)
                return 1;
            else if(n1==0&&n2==1)
                return 0;
            else
                continue;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值