您的位置:首页 > 其它

车牌摇号 程序设计题

2016-09-02 18:40 141 查看
阿里面试题(车牌摇号问题)
题目大意:由于北京车牌紧张,如果需要车牌,需要在每月中旬进行摇号来获得车牌,获得车牌的概率由下面式子决定 money/100000. (money就是摇号时支付的预约金),如果摇中,与预约金用来买车牌,如果没有摇中,这预约金退回。如果你希望在6个月中摇到车牌,问在规定的时间摇到车牌,最少的期望金额是多少?

对于这个问题,可以直接用数学公式进行求解,可以列出如下计算公式:
(x1 * x1/100000)+ ( 1 - x1/100000) * (x2 * x2 /100000) + (1 - x1/100000) * (1 - x2/100000) * (x3 * x3 / 100000) + (1 - x1/100000) * (1 - x2/100000) *
(1 - x3/100000)*(x4* x4 / 100000) + (1 - x1/100000) * (1 - x2/100000) * (1 - x3/100000)*(1 - x4/100000)*(x5* x5 / 100000) +
(1 - x1/100000) * (1 - x2/100000) * (1 - x3/100000)*(1 - x4/100000)*(1 - x5/100000)*(100000) 


对于求解上式,可以通过SGD (随机梯度下降法进行求解),代码如下:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <map>
#include <cmath>
#include <time.h>
#include <random>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;

#define inf 100000000
// ctrl + k and then press c 注释
// ctrl + k and then press u 取消注释
// ctrl + shift + L 删除当前行

// 用SGD求解 如下函数
// f(x0,x1,x2,x3,x4,x5) = x1 * x1 / money + (1 - x1/money) * x2 * x2 / money + (1 - x1/money) * (1 - x2/money) * x3 * x3 / money + ... + (1 - x1/money) * ...*(1 - x4/money) * x5 * x5 / money
double lamba,money;
double x[6];
// 控制变量值的范围
void Update(double& a,double gradient,double lamba)
{
//printf("a = %lf and gradient = %lf and lamba = %lf\n",a,gradient,lamba);
double result = a - gradient * lamba;
if(result >= 0 && result <= money)
a = result;
}

// (1 - x1/money) * (1 - x2/money) * (1 - x3/money) * (1 - x4/money) * ...*(1 - xn/money)
// excp 表示不乘以(1 - Xexcp/money)
double cal_1_to_n(int n,int excp)
{
double tmp = 1.0;
for(int i = 0;i<=n;i++)
if(i != excp)
{
tmp *= (1 - x[i] / money);
}
return tmp;
}

int main()
{
double steps;

printf("Please Input money = ");
scanf("%lf",&money);  //在题目中就100000

printf("Please Input steps = ");
scanf("%lf",&steps); // 随机梯度迭代次数

printf("Please Input lamba = ");
scanf("%lf",&lamba);  //梯度下降时的步长

srand((unsigned)time(NULL));   // 随机种子

// 变量随机值
for(int i = 0 ;i< 6;i++)
x[i] = rand() % (int(money) + 1);
x[5] = money;

double gradient;
for(int i = 0;i<steps;i++)
{
for(int j = 0;j<5;j++)
{
// 计算梯度值
gradient = 2.0 * x[j] / money;
gradient *= cal_1_to_n(j - 1,10);

double tmp = 1.0;
for(int k = j + 1;k<=5;k++)
{
tmp = cal_1_to_n(k-1,j);
tmp *= x[k] * x[k] / money;
tmp /= money;
gradient -= tmp;
}

/*printf("steps = %d\n",i);
printf("x[%d] gradient is %lf\n",j,gradient);*/
// 更新梯度值
Update(x[j],gradient,lamba);
}

double min_val = 0.0;

// 计算期望值
for(int i = 0;i<6;i++)
{
min_val += cal_1_to_n(i-1,10) * x[i] * x[i] / money;
}

//每迭代100次 打印输出

if(i % 100 == 0)
{
for(int i = 0;i<6;i++)
printf("x[%d] =  %lf ",i,x[i]);
printf("\n");
printf("the min_val is %lf\n",min_val);
}
}
return 0;
}


除了这种通过数学求解,我们也可以通过动态规划的方式进行求解。如果我们考虑所有情况,时间复杂度太大,不过我们可以缩减动态规划的空间,让结果在可控范围内就行,代码如下:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;

#define inf 100000000
// ctrl + k and then press c 注释
// ctrl + k and then press u 取消注释
// ctrl + shift + L 删除当前行

int main()
{
int precision,money;
printf("Please Input precision = ");
scanf("%d",&precision);
printf("Please Input money = ");
scanf("%d",&money);
double **dp = new double* [6];

for(int i = 0;i<6;i++)
dp[i] = new double[precision + 1];

//dp[i][j] 表示第i轮过后,在没有拿到拍照的概率为j的情况下,花费最少的钱。

for(int i = 0;i<6;i++)
for(int j = 0;j<=precision;j++)
dp[i][j] = inf;

dp[0][precision] = 0;
for(int i = 0; i < 5;i++)
{
for(int j = 0;j<=precision;j++)
{
if(dp[i][j] < inf - 1)
{
for(int k = 0;k<=money;k++)
{
int no_card_rate = (int)(j - k * 1.0 * j/ money); // 对概率这一维进行压缩,只保留1/precision 的精度。
dp[i+1][no_card_rate] = min(dp[i+1][no_card_rate],dp[i][j] + (k * 1.0 * k * j /precision/ money));
}
}
}
}

double Min_val = inf;

for(int i = 0;i<=precision;i++)
{
Min_val = min(Min_val,dp[5][i] + (i * 1.0/precision) * money);
}

cout << Min_val << endl;

return 0;
}


结论:如果必须在6个月之内拿到拍照,期望金额是 100000 * 40%。可以思考,最后的期望金额只跟时间有关,跟100000没什么关系。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: