zoukankan      html  css  js  c++  java
  • 【IT笔试面试题整理】把n个骰子扔在地上,所有骰子朝上一面的点数之和为S概率转

    题目:n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。

    分析:玩过麻将的都知道,骰子一共6个面,每个面上都有一个点数,对应的数字是1 6之间的一个数字。所以,n个骰子的点数和的最小值为n,最大值为6n。因此,一个直观的思路就是定义一个长度为6n-n的数组,和为S的点数出现的次数保存到数组第S-n个元素里。另外,我们还知道n个骰子的所有点数的排列数6^n。一旦我们统计出每一点数出现的次数之后,因此只要把每一点数出现的次数除以n^6,就得到了对应的概率。

    该思路的关键就是统计每一点数出现的次数。要求出n个骰子的点数和,我们可以先把n个骰子分为两堆:第一堆只有一个,另一个有n-1个。单独的那一个有可能出现从16的点数。我们需要计算从16的每一种点数和剩下的n-1个骰子来计算点数和。接下来把剩下的n-1个骰子还是分成两堆,第一堆只有一个,第二堆有n-2个。我们把上一轮那个单独骰子的点数和这一轮单独骰子的点数相加,再和剩下的n-2个骰子来计算点数和。分析到这里,我们不难发现,这是一种递归的思路。递归结束的条件就是最后只剩下一个骰子了。

     

     

     1 int g_maxValue = 6;
     2   
     3  void PrintSumProbabilityOfDices_1(int number)
     4  {
     5      if(number < 1)
     6          return;
     7   
     8      int maxSum = number * g_maxValue;
     9      int* pProbabilities = new int[maxSum - number + 1];
    10      for(int i = number; i <= maxSum; ++i)
    11          pProbabilities[i - number] = 0;
    12   
    13      SumProbabilityOfDices(number, pProbabilities);
    14   
    15      int total = pow((float)g_maxValue, number);
    16      for(int i = number; i <= maxSum; ++i)
    17      {
    18          float ratio = (float)pProbabilities[i - number] / total;
    19          printf("%d: %f\n", i, ratio);
    20      }
    21   
    22      delete[] pProbabilities;
    23  }
    24   
    25  void SumProbabilityOfDices(int number, int* pProbabilities)
    26  {
    27      for(int i = 1; i <= g_maxValue; ++i)
    28          SumProbabilityOfDices(number, number, i, 0, pProbabilities);
    29  }
    30   
    31  void SumProbabilityOfDices(int original, int current, int value, int tempSum, int* pProbabilities)
    32  {
    33      if(current == 1)
    34      {
    35          int sum = value + tempSum;
    36          pProbabilities[sum - original]++;
    37      }
    38      else
    39      {
    40          for(int i = 1; i <= g_maxValue; ++i)
    41          {
    42              int sum = value + tempSum;
    43              SumProbabilityOfDices(original, current - 1, i, sum, pProbabilities);
    44          }
    45      }
    46  }

     

    上述算法当number比较小的时候表现很优异。但由于该算法基于递归,它有很多计算是重复的,从而导致当number变大时性能让人不能接受。关于递归算法的性能讨论,详见本博客系列的第16

    我们可以考虑换一种思路来解决这个问题。我们可以考虑用两个数组来存储骰子点数每一总数出现的次数。在一次循环中,第一个数组中的第n个数字表示骰子和为n出现的次数。那么在下一循环中,我们加上一个新的骰子。那么此时和为n的骰子出现的次数,应该等于上一次循环中骰子点数和为n-1n-2n-3n-4n-5n-6的总和。所以我们把另一个数组的第n个数字设为前一个数组对应的第n-1n-2n-3n-4n-5n-6之和。基于这个思路,我们可以写出如下代码:

     

     1 void PrintSumProbabilityOfDices_2(int number)
     2  {
     3      double* pProbabilities[2];
     4      pProbabilities[0] = new double[g_maxValue * number + 1];
     5      pProbabilities[1] = new double[g_maxValue * number + 1];
     6      for(int i = 0; i < g_maxValue * number + 1; ++i)
     7      {
     8          pProbabilities[0][i] = 0;
     9          pProbabilities[1][i] = 0;
    10      }
    11   
    12      int flag = 0;
    13      for (int i = 1; i <= g_maxValue; ++i)
    14          pProbabilities[flag][i] = 1;
    15        
    16      for (int k = 2; k <= number; ++k)
    17      {
    18          for (int i = k; i <= g_maxValue * k; ++i)
    19          {
    20              pProbabilities[1 - flag][i] = 0;
    21              for(int j = 1; j <= i-k+1 && j <= g_maxValue; ++j)
    22                  pProbabilities[1 - flag][i] += pProbabilities[flag][i - j];
    23          }
    24   
    25          flag = 1 - flag;
    26      }
    27   
    28      double total = pow((double)g_maxValue, number);
    29      for(int i = number; i <= g_maxValue * number; ++i)
    30      {
    31          double ratio = pProbabilities[flag][i] / total;
    32          printf("%d: %f\n", i, ratio);
    33      }
    34   
    35      delete[] pProbabilities[0];
    36      delete[] pProbabilities[1];
    37  }

     

     值得提出来的是,上述代码没有在函数里把一个骰子的最大点数硬编码(hard code)6,而是用一个变量g_maxValue来表示。这样做的好处时,如果某个厂家生产了最大点数为4或者8的骰子,我们只需要在代码中修改一个地方,扩展起来很方便。如果在面试的时候我们能对面试官提起对程序扩展性的考虑,一定能给面试官留下一个很好的印象。

    以上摘自何海涛博客

    注意上面方法二中21行红色的条件判断,此处原文写的是

    for(int j = 1; j <= i && j <= g_maxValue; ++j)

    这样的话,需要每次将前k-1元素置为0。原文评论有人指出这个问题,博主也回复说已经修改了,但是文中的代码并没有修改。

    转:http://www.cnblogs.com/wolenski/archive/2012/08/10/2633128.html 

     

     

  • 相关阅读:
    优先队列
    Problem W UVA 662 二十三 Fast Food
    UVA 607 二十二 Scheduling Lectures
    UVA 590 二十一 Always on the run
    UVA 442 二十 Matrix Chain Multiplication
    UVA 437 十九 The Tower of Babylon
    UVA 10254 十八 The Priest Mathematician
    UVA 10453 十七 Make Palindrome
    UVA 10163 十六 Storage Keepers
    UVA 1252 十五 Twenty Questions
  • 原文地址:https://www.cnblogs.com/WayneZeng/p/3050933.html
Copyright © 2011-2022 走看看