左程云算法菜手班整理(二)
左程云算法新手班
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-7,则直接让7减去1变为 0-6,然后0-6需要用三个二进制位才能表示(三位可以表示0-7,所以能包含0~6)
- 则只要制造一个函数,使其等概率返回0或者1,然后三个返回值拼凑到三个二进制位上,则就能保住同概率。 注意 0~7中的7不返回,继续调用此函数。
- 给你 1~5,则是 1 2 3 4 5 ,取中间值3不返回,1、2返回0;4,5返回1;写出此函数假设为 b函数
- 给定的函数假设为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;
}
}
};