C++ C++;:使用数组创建函数

C++ C++;:使用数组创建函数,c++,C++,编写一个具有以下功能的函数: input: array of pairs (unique id and weight) length of N, K =< N output: K random unique ids (from input array) 输入:对数组(唯一id和权重)长度为N,K=i)输入权重中减去权重,并求和权重 将输入[i]移动到位置[n-1](将中间的元素向下滑动一个插槽)。这是一个昂贵的部分,因为它是O(n),我们要做K次。您可以在最后一次迭代中跳过这一步

编写一个具有以下功能的函数:

input: array of pairs (unique id and weight) length of N, K =< N  
output: K random unique ids (from input array)  
输入:对数组(唯一id和权重)长度为N,K=
注意:被多次调用在输出中出现某些Id的频率应该越高,它的权重就越大。 示例:权重为5的id在输出中出现的频率应该是权重为1的id的5倍。此外,分配的内存量应在编译时已知,即不应分配额外的内存

我的问题是:如何解决这个任务

编辑
谢谢大家的回复

目前,我无法理解配对的重量如何影响配对在输出中的出现频率,您能给我更清楚的“for dummy”解释它是如何工作的吗?

好的,因此您得到如下输入:

(3, 7)
(1, 2)
(2, 5)
(4, 1)
(5, 2)
您希望选择一个随机数,以便每个id的权重反映在选择中,即从以下列表中选择一个随机数:

3 3 3 3 3 3 3 1 1 2 2 2 2 2 4 5 5
最初,我创建了一个临时数组,但这也可以在内存中完成,您可以通过将所有权重相加=X来计算列表的大小,在本例中为=17

在[0,X-1]之间选择一个随机数,通过循环列表,对权重进行累加,计算应该返回哪个id。假设我有一个随机数8

(3, 7) total = 7 which is < 8
(1, 2) total = 9 which is >= 8 **boom** 1 is your id!

这是一个好问题:)

此解决方案适用于非整数权重,并使用常量空间(即:空间复杂度=O(1))。不过,它确实修改了输入数组,但最后唯一的区别是元素的顺序不同

  • 将每个输入的权重添加到以下输入的权重中,从底部开始逐步向上。现在,每个权重实际上是该输入的权重和之前所有权重的总和

  • sum_weights=所有权重之和,n=n

  • K次:

    • 选择范围为[0,加权和]的随机数r

    • 二进制搜索(现在求和)权重大于或等于r,i的第一个插槽的前n个元素

    • 将输入[i].id添加到输出

    • 减去输入[i-1]。从输入[i]中减去权重。权重(除非i==0)。现在减去输入[i]。从以下(>i)输入权重中减去权重,并求和权重

    • 将输入[i]移动到位置[n-1](将中间的元素向下滑动一个插槽)。这是一个昂贵的部分,因为它是O(n),我们要做K次。您可以在最后一次迭代中跳过这一步

    • 从n中减去1

  • 通过减去前面输入的权重,将所有权重从n-1减到1

时间复杂度是O(K*N)。昂贵的部分(时间复杂度)是洗牌选择的元素。我怀疑有一个聪明的方法来避免这种情况,但还没有想到任何事情

更新 不清楚“output:K random unique id”是什么意思。上面的解决方案假设这意味着输出id应该是唯一的/不同的,但如果不是这样,那么问题就更简单了:

  • 将每个输入的权重添加到下一个输入的权重中,从底部开始向上计算。现在每个权重实际上是该输入的权重和之前所有权重的总和

  • sum_weights=所有权重之和,n=n

  • K次:

    • 选择范围为[0,加权和]的随机数r

    • 二进制搜索(现在求和)权重大于或等于r,i的第一个插槽的前n个元素

    • 将输入[i].id添加到输出

  • 通过减去前面输入的权重,将所有权重从n-1减到1


时间复杂度为O(K*log(N))。

假设有足够好的随机数生成器:

  • 将重量相加(
    总重量
  • 重复K次:
    • 选择一个介于0和
      总重量
      之间的数字(
      选择
    • 查找从数组开始到该对的所有权重之和大于或等于
      选择的第一对
    • 将该对的第一部分写入输出

您需要足够的存储空间来存储总重量。

我确实假设输出中的ID必须是唯一的。这使得此问题成为随机采样问题的一个特定实例

我能想到的第一种方法是在O(N^2)时间内解决这个问题,使用O(N)内存(输入数组本身加上常量内存)。 我假设权重是正的

设A为对的数组

1) 将N设置为A长度

2) 计算所有权重W的总和

3) 循环K次

3.1)r=rand(0,W)


3.2)在A上循环并找到第一个索引i,这样A[1].w+…+A[i].w我的简短回答是:绝对不会

仅仅因为问题定义不正确。正如我们所注意到的:


需求中有一点矛盾。它表明K您的问题到底是什么?结果ID的顺序重要吗?即[1,2]和[2,1]被认为是不同的结果?@Sergey,@javamonkey79请求可能没有明确说明,但我认为很明显:回答这个面试问题。要求中有一点矛盾。它说明K更正:没有有趣的面试问题。@Vic没有“将原始数组的副本创建到hastable中”打破了“分配的内存数量在编译时应该是已知的,即不应该分配额外的内存”的要求,这是O(NK),并且您正在为哈希表分配内存,OP说这是不允许的(我想他指的是O(1)空间)
O(n) : create copy of original array into hastable +
(
   O(n) : calculate sum of weights +
   O(1) : calculate random between range +
   O(n) : cumulative totals
) * K random pickings
= O(n*k) overall
#include <iostream>
#include <cstdlib>
#include <ctime>

// 0 - id, 1 - weight
typedef unsigned Pair[2];

unsigned Random(Pair* i_set, unsigned* i_indexes, unsigned i_size)
{
   unsigned sumOfWeights = 0;
   for (unsigned i = 0; i < i_size; ++i)
   {
      const unsigned index = i_indexes[i];
      sumOfWeights += i_set[index][2];
   }

   const unsigned random = rand() % sumOfWeights + 1;

   sumOfWeights = 0;
   unsigned i = 0;
   for (; i < i_size; ++i)
   {
      const unsigned index = i_indexes[i];
      sumOfWeights += i_set[index][3];
      if (sumOfWeights >= random)
      {
         break;
      }
   }

   return i;
}
template<unsigned N, unsigned K>
void Generate(Pair (&i_set)[N], unsigned (&o_res)[K])
{
   unsigned deck[N];
   for (unsigned i = 0; i < N; ++i)
   {
      deck[i] = i;
   }

   unsigned max = N - 1;

   for (unsigned i = 0; i < K; ++i)
   {
      const unsigned index = Random(i_set, deck, max + 1);

      std::swap(deck[max], deck[index]);
      o_res[i] = i_set[deck[max]][0];
      --max;
   }
}
int main()
{
   srand((unsigned)time(0));

   const unsigned c_N = 5;    // N
   const unsigned c_K = 2;    // K
   Pair input[c_N] = {{0, 5}, {1, 3}, {2, 2}, {3, 5}, {4, 4}}; // input array
   unsigned result[c_K] = {};

   const unsigned c_total = 1000000; // number of iterations
   unsigned counts[c_N] = {0};       // frequency counters

   for (unsigned i = 0; i < c_total; ++i)
   {
      Generate<c_N, c_K>(input, result);
      for (unsigned j = 0; j < c_K; ++j)
      {
         ++counts[result[j]];
      }
   }

   unsigned sumOfWeights = 0;
   for (unsigned i = 0; i < c_N; ++i)
   {
      sumOfWeights += input[i][1];
   }

   for (unsigned i = 0; i < c_N; ++i)
   {
      std::cout << (double)counts[i]/c_K/c_total  // empirical frequency
                << " | "
                << (double)input[i][1]/sumOfWeights // expected frequency
                << std::endl;
   }

   return 0;
}
N = 5, K = 2

      Frequencies
Empiricical | Expected
 0.253813   | 0.263158
 0.16584    | 0.157895
 0.113878   | 0.105263
 0.253582   | 0.263158
 0.212888   | 0.210526
N = 5, K = 5

      Frequencies
Empiricical | Expected
 0.2        | 0.263158
 0.2        | 0.157895
 0.2        | 0.105263
 0.2        | 0.263158
 0.2        | 0.210526