对数组中的最大数量的索引进行采样,概率为1 /(最大数量的数量)

时间:2014-02-12 02:34:51

标签: algorithm

这是我最近面临的一个面试问题。程序返回数组中最大数字的索引[注意:数组可能包含也可能不包含多个最大数字副本],这样每个索引(包含最大数字)的概率为1 /最大数字被退回。

示例:

  • [ - 1 3 2 3 3],每个职位[1,3,4]的概率为1/3(三个3)
  • [2 4 6 6 3 1 6 6],每个[2,3,6,7]都有1/4的概率返回(对应于6s的位置)。

首先,我给出了O(n)时间和O(n)空间算法,我收集了最大索引集,然后从集合中返回一个随机数。但他要求O(n)时间和O(1)复杂度计划,然后我想出了这个。

int find_maxIndex(vector<int> a)
{
     max = a[0];
     max_index = 0;
     count = 0;

     for(i = 1 to a.size())
     {
         if(max < a[i])
         {
             max = a[i];
             count = 0;
         }
         if(max == a[i])
         {
              count++;
              if(rand < 1/count) //rand = a random number in the range of [0,1]
                  max_index = i;
         }
      }
      return max_index;
}

我给了他这个解决方案。但我怀疑的是,这个程序是否会以相同的概率选择最大数的索引之一。希望我很清楚。有没有其他方法可以做到这一点?

3 个答案:

答案 0 :(得分:3)

您的算法运行正常,您可以通过归纳证明它。

也就是说,假设它适用于任何大小为N的数组,证明它适用于任何大小为N+1的数组。

因此,给定一个大小为N+1的数组,将其视为大小为N的子数组,最后跟随一个新元素。通过假设,您的算法统一选择子数组中的一个最大元素......然后它的行为如下:

如果新元素大于子数组的最大值,则返回该元素。这显然是正确的。

如果新元素小于子数组的最大值,则返回子数组上算法的结果。也显然是正确的。

唯一稍微棘手的部分是当新元素等于子数组的max元素时。在这种情况下,让子数组中的最大元素数为k。然后,根据假设,您的算法选择其中一个概率1/k。通过保持概率为k/(k+1)的相同元素,您可以根据需要选择相同元素的总概率等于1/k * k /(k+1) == 1/(k+1)。你也选择了具有相同概率的最后一个元素,所以我们完成了。

要完成归纳证明,只需验证算法是否适用于大小为1的数组。另外,为了实现质量,请修复它不要在大小为零的数组上崩溃: - )

[更新]

顺便提一下,这个算法及其证明与Fisher-Yates shuffle密切相关(我一直认为是“Knuth的卡片改组算法”,但维基百科说我落后于时代)。

答案 1 :(得分:3)

你拥有的是Reservoir sampling!还有另一种易于理解的解决方案,但需要两次通过。

int find_maxIndex(vector<int> a){
    int count = 1;
    int maxElement = a[0];
    for(int i = 1; i < a.size(); i++){
        if(a[i] == maxElement){
            count ++;
        } else if(a[i] > maxElement){
            count = 1;
            maxElement = a[i];
        }
    }
    int occurrence = rand() % count + 1;
    int occur = 0;
    for(int i = 0; i < a.size(); i++){
        if(a[i] == maxElement){
            occur++;
            if(occur == occurrence) return i;
        }
    }
}

算法非常简单,首先查找第一遍中出现最大元素的次数。并选择一个随机事件并返回该事件的索引。虽然需要两次通过,但很容易理解。

答案 2 :(得分:1)

这个想法是合理的,但魔鬼在细节中。

首先,您使用的语言是什么?它可能会有所作为。来自C和C ++的rand()将返回一个整数,除非返回1/count,否则该整数不可能小于0。即便如此,如果1/count是整数除法,那么结果总是0

此外,您的计数结果为1.当您获得新的最大值时,它会以1开头,但您会立即在下一个if语句中将其递增。