【问题标题】:Generate random index different then any one given in a set生成与集合中给定的任何索引不同的随机索引
【发布时间】:2013-11-02 16:04:36
【问题描述】:

我想生成一个从0size - 1 的统一索引,但不同于给定集合excluded 中的任何索引。

size 大约是 100。通常有 1、2 或 3 个 exluded 索引。 excluded 中的索引是唯一的且未排序。

理想情况下,标题将带有多个参数,可能是这样的:

int getRandomIndex(Random rand, int size, int... i)

或者,如果这个... 多参数很慢(是吗?)我们可以传递简单的int[] excluded 数组或其他东西。

如何快速完成? getRandomIndex() 被调用了数百万次。

【问题讨论】:

  • 那么你想生成一个随机数,但它不包含在某些数字集中?
  • @JamesMontagne 是的,完全正确。
  • @JamesMontagne 比大小差。
  • 您可以将所有有效索引添加到集合中,然后从该集合中随机选取一个元素。当然,如果有效索引的数量很大,那就没有效率了。
  • 如果你关心构建一个数组,那就构建一个Set吧!

标签: java performance algorithm


【解决方案1】:
static int getRandomIndex(Random rand, int size, Integer... excludes) {
    List<Integer> excludeList = Arrays.asList(excludes);
    int number;
    do {
        number = rand.nextInt(size);
    } while (excludeList.contains(number));

    return number;
}

【讨论】:

    【解决方案2】:

    您可以分配可能值 0..size-1 的数组,删除所有已知索引并从其余元素中选择随机元素。

    如果索引数组已排序并且所有值都是唯一的,则无需中间值数组即可:

    int getRandomIndex(Random rand, int size, int... ii)
    {
      int result = rand.nextInt(size - ii.length);
      for(int i : ii) {
        if(result >= i) {
          ++result; 
        }
      }
    
      return result;
    }
    

    编辑:我在 if 条件中发现错误,已更正。

    编辑:

    int getRandomIndex(Random rand, int size, int i) {
      int result = rand.nextInt(size - 1);
      if(result >= i) {
        result++;
      }
    }
    
    int getRandomIndex(Random rand, int size, int i, int j) {
      int result = rand.nextInt(size - 2);
      if(i > j) {
        int tmp = j;
        j = i;
        i = tmp;
      }
      if(result >= i) {
        result++;
      }
      if(result >= j) {
        result++;
      }
      return result;
    }
    

    和 (i,j,k) 的类似函数。

    只调用一次rand.next 的函数可能比任何要求随机数多次的函数更快。

    【讨论】:

    • 使用rand.nextInt(size - ii.length)而不是rand.nextInt() % (size - ii.length),否则结果将不会均匀分布(尤其是如果尺寸很大)
    • 唯一性很好,你可以简单地在getRandomIndex的第一个语句中添加Arrays.sort调用。
    • @Nikolay:您的解决方案为排除元素之后的元素提供了双倍概率。如果排除 1 和 2,则 3 三倍的概率。
    • @maaartinus:让我们检查一下。 size == 4, 1,2 被排除在外。 rand.nextInt(size - 2) 可以产生 0 或 1,0 保持不变,1 更改为 3。这两个值的概率为 1/2。
    • @Nikolay:但是如果你排除 0 和 1,那么你总是得到 2。
    【解决方案3】:

    我会做一些不同的事情。要选择小于 size 但不是 i 或 j 的随机数,您可以选择介于 0 和 size-3 之间的随机数。如果该数字为 i,则返回 size-2,如果该数字为 j,则返回 size-1。否则返回原始随机数。

    存在 i 或 j 大小在 2 以内的极端情况,但我将其留给感兴趣的读者。其实我觉得还是可以的。

    这可以概括为选择一个小于大小且不在整数数组中的数字。选择一个小于 size 的数字 - 数组长度 - 1。

    【讨论】:

    • 我明白了。我认为这是最好的解决方案。代码很简单int result = rand.nextInt(size - ii.length) for(int i = 0; i &lt; ii.length; ++i) { if(ii[i] == result) return size - i - 1; } return result;
    • @Nikolay 不适用于getRandomIndex(random, 5, 1,2,4)(生成04 而不是03)。
    • 是的。有一个麻烦。排除索引和尾部之间的排列可能会像您的示例一样耦合。
    【解决方案4】:

    您可以使用Set 来快速查找

    int getRandomIndex(Random rand, int size, Integer... i) {
        return getRandomIndex(rand, size, new HashSet<Integer>(Arrays.asList(i)));
    }
    
    
    int getRandomIndex(Random rand, int size, Set<Integer> x) {
        int result;
        do {
            result = (int) (rand.nextDouble() * size);
        } while (x.contains(result));
        return result;
    }
    

    如果所有索引的集合都是预定义的,那么您可以使用 Set x final static 并在每次调用该方法时备用。

    EIDT

    好吧,如果性能是一个问题,那么我会说您使用while(r == i || r == j || r == k) 的第一种方法既不差也不丑,只需对您的方法进行三个重载并调用您需要的适当的重载,调用看起来与使用 val-len-array。

    您的评估速度不会比(r == i || r == j || r == k) 更快

    int getRandomIndex(Random rand, int size, int i)
    int getRandomIndex(Random rand, int size, int i, int j)
    int getRandomIndex(Random rand, int size, int i, int j, int k)
    

    【讨论】:

      【解决方案5】:

      假设许多连续调用使用相同的大小和排除值...

      • 首先,初始化一个包含所有允许索引的数组
      • getRandomIndex 被调用时,只需选择这个数组的一个随机元素

      代码:

      int[] indices;
      
      void init(int size, Integer... excluded)
      {
         indices = new int[size-excluded.length];
         int pos = 0;
         Set<Integer> excludedSet = new HashSet<Integer>(Arrays.asList(excluded));
         for (int i = 0; i < size; i++)
         {
            if (!excludedSet.contains(i))
            {
               indices[pos++] = i;
            }
         }
      }
      
      int getRandomIndex(Random random)
      {
         return indices[random.nextInt(data.length)];
      }
      

      示例用法:

      init(100, 50, 23, 10);
      Random random = new Random();
      for (int i = 0; i < 200; i++)
      {
         System.out.println(getRandomIndex(random));
      }
      

      【讨论】:

        猜你喜欢
        • 2014-01-11
        • 1970-01-01
        • 1970-01-01
        • 2014-02-22
        • 2014-03-23
        • 1970-01-01
        • 2021-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多