【问题标题】:Picking random indexes into a sorted array将随机索引挑选到排序数组中
【发布时间】:2018-08-23 04:56:01
【问题描述】:

假设我有一个排序的值数组:

int n=4; // always lower or equal than number of unique values in array
int i[256] = {};
int v = {1 1 2 4 5 5 5 5 5 7 7 9 9 11 11 13}
// EX 1        ^         ^       ^       ^
// EX 2    ^                 ^         ^ ^
// EX 3    ^ ^           ^               ^

我想生成 n 个随机索引值i[0] ... i[n-1],这样:

  1. v[i[0]] ... v[i[n-1]] 指向一个唯一的数字(即不能两次指向 5)
  2. 每个数字必须是同类中最右边的(即必须指向最后一个 5)
  3. 应始终包含指向最终数字(在本例中为 13)的索引。

到目前为止我已经尝试过:

  1. 获取最后一个唯一值的索引
  2. 改组索引
  3. 挑选出前 n 个索引

我在 C 中实现这一点,所以我可以依赖的标准 C 函数越多,代码越短越好。 (例如,shuffle 不是标准的 C 函数,但如果我必须,我必须。)

【问题讨论】:

  • 如果发现唯一值不够怎么办?
  • 与数组的大小相比,n 小吗?请给出一些示例尺寸。这与判断效率有关。例如。对于大小 1000 和 n==3,任何对整个数组(唯一值)进行洗牌的东西都是低效的。而对于大小 1000 和 n=100,我希望改组(例如当前存在的答案)比例如更快。跟踪已经使用的索引和值(这似乎是直接的方法)。 (@user3386109 我希望我不会攻击您的解决方案。)
  • 您是在询问算法还是如何在 C 中执行此操作?这是两个独立的问题,前者可能更适合计算机科学网站。后者是题外话,因为不清楚你不明白什么。总的来说,这太宽泛了。
  • 为什么说“洗牌不是线性的”?它是线性的。
  • 是否有某种原因,为什么会出现多次相同的值?输入的数量会影响选择数字的概率吗?

标签: c arrays random


【解决方案1】:

创建一个包含最后一个索引值的数组

int last[] = { 1, 2, 3, 8, 10, 12, 14 };

Fisher-Yates shuffle 数组。

从混洗后的数组中取出第一个 n-1 元素。

将索引添加到最终数字。

如果需要,对结果数组进行排序。

【讨论】:

    【解决方案2】:

    此算法称为reservoir sampling,只要您知道需要多大的样本但不知道要从多少个元素中采样,就可以使用该算法。 (这个名字来源于你总是保持一个正确数量的样本的容器。当一个新的值进来时,你将它混合到容器中,删除一个随机元素,然后继续。)

    1. 创建大小为n的返回值数组sample
    2. 开始扫描输入数组。每次找到新值时,将其索引添加到 sample 的末尾,直到有 n 采样元素。
    3. 继续扫描数组,但现在当你找到一个新值时:

      一个。在 [0, i) 范围内选择一个随机数 r,其中 i 是到目前为止看到的唯一值的数量。

      b.如果r 小于n,则用新元素覆盖元素r

    4. 当你走到最后,对sample进行排序,假设你需要对它进行排序。

    为确保您始终拥有样本中的最后一个元素,请运行上述算法以选择大小为 n-1 的样本。仅在找到更大的元素时才考虑新元素。

    该算法在v 的大小上是线性的(加上最后一步排序的n log n 术语。)如果您已经拥有每个值的最后索引列表,则有更快的算法(但随后在开始采样之前,您会知道宇宙的大小;如果您不知道,水库采样主要是有用的。)

    事实上,它与收集所有索引然后找到 Fisher-Yates shuffle 的前缀在概念上没有区别。但是它使用 O(n) 临时内存,而不是足够存储整个索引列表,这可能被认为是一个加号。

    这是一个未经测试的示例 C 实现(需要您编写函数 randrange()):

    /* Produces (in `out`) a uniformly distributed sample of maximum size
     * `outlen` of the indices of the last occurrences of each unique
     * element in `in` with the requirement that the last element must
     * be in the sample.
     * Requires: `in` must be sorted.
     * Returns: the size of the generated sample, while will be `outlen` 
     *          unless there were not enough unique elements.
     * Note: `out` is not sorted, except that the last element in the
     *       generated sample is the last valid index in `in`
     */
    size_t sample(int* in, size_t inlen, size_t* out, size_t outlen) {
      size_t found = 0;
      if (inlen && outlen) {
        // The last output is fixed so we need outlen-1 random indices
        --outlen; 
        int prev = in[0];
        for (size_t curr = 1; curr < inlen; ++curr) {
          if (in[curr] == prev) continue;
          // Add curr - 1 to the output
          size_t r = randrange(0, ++found);
          if (r < outlen) out[r] = curr - 1;
          prev = in[curr];
        }
        // Add the last index to the output
        if (found > outlen) found = outlen;
        out[found] = inlen - 1;
      }
      return found;
    }
    

    【讨论】:

    • 您可以将您的水库保存在数组中,该数组将与结果选择一起返回。这将不需要额外的临时内存。 (如果我的算法正确)
    • 啊..我想我现在明白了...这是一个绝妙的方法:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多