惰性洗牌算法

2023-11-21

我有一个很大的元素列表,我想以随机顺序迭代它们。但是,我无法修改该列表,也不想创建它的副本,因为 1) 它很大,2) 可以预期迭代会提前取消。

List<T> data = ...;
Iterator<T> shuffled = shuffle(data);
while (shuffled.hasNext()) {
    T t = shuffled.next();
    if (System.console().readLine("Do you want %s?", t).startsWith("y")) {
        return t;
    }
}
System.out.println("That's all");
return t;

我正在寻找一种算法,上面的代码可以在O(n)(并且最好只需要O(log n)space),因此缓存之前生成的元素不是一种选择。我不在乎算法是否有偏见(只要它不明显)。

(我在问题中使用了伪Java,但如果您愿意,也可以使用其他语言)

这是迄今为止我得到的最好的。

Iterator<T> shuffle(final List<T> data) {
    int p = data.size();
    while ((data.size() % p) == 0) p = randomPrime();
    return new Iterator<T>() {
        final int prime = p;
        int n = 0, i = 0;
        public boolean hasNext() { return i < data.size(); }
        public T next() {
            i++; n += prime;
            return data.get(n);
        }
    }
}

迭代中的所有元素O(n),恒定空间,但显然有偏差,因为它只能产生data.size()排列。


我所知道的最简单的洗牌方法与索引一起使用。如果List不是一个ArrayList,如果您尝试使用以下之一(aLinkedList确实有一个get通过 ID,但它是 O(n),所以你最终会得到 O(n^2) 时间)。

如果 O(n) 空间没问题(我假设不是),我会推荐费舍尔-耶茨/高德纳洗牌,时间复杂度为 O(n),并且易于实现。您可以对其进行优化,这样您只需执行单个操作即可获取第一个元素,但您需要随时跟踪修改后的列表的其余部分。

我的解决方案:

好吧,所以这根本不是很随机,但如果你想要小于 O(n) 的空间,我看不到更好的方法。

它需要 O(1) 空间和 O(n) 时间。

可能有一种方法可以稍微提高空间使用量并获得更多随机结果,但我还没有弄清楚。

这与相对素数。这个想法是,给定 2 个相对素数a(发电机)和b,当你循环通过a % b, 2a % b, 3a % b, 4a % b,...,您将看到每个整数 0、1、2、...、b-2、b-1,并且这也会在看到任何整数两次之前发生。不幸的是,我没有证明的链接(维基百科链接可能提到或暗示它,我没有检查太多细节)。

我首先增加长度,直到得到一个素数,因为这意味着任何其他数字都将是相对素数,这限制要少得多(并且只需跳过任何大于原始长度的数字),然后生成一个随机数数,并将其用作生成器。

我正在迭代并打印出所有值,但应该很容易修改以在给定当前值的情况下生成下一个值。

注意我正在跳过1 and len-1和我的nextInt,因为这些会产生1,2,3,... and ...,3,2,1分别,但您可以包含这些,但如果长度低于某个阈值,则可能不会包含这些。

您可能还想生成一个随机数,将生成器乘以(模长度)作为起始值。

Java代码:

static Random gen = new Random();

static void printShuffle(int len)
{
  // get first prime >= len
  int newLen = len-1;
  boolean prime;
  do
  {
     newLen++;
     // prime check
     prime = true;
     for (int i = 2; prime && i < len; i++)
        prime &= (newLen % i != 0);
  }
  while (!prime);

  long val = gen.nextInt(len-3) + 2;
  long oldVal = val;
  do
  {
     if (val < len)
        System.out.println(val);
     val = (val + oldVal) % newLen;
  }
  while (oldVal != val);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

惰性洗牌算法 的相关文章

  • 以编程方式分解大量数字

    好吧 所以我有一个巨大的数字f 实际上 这个数字只有 100 多位数字长 我知道这些因子的大小大致相同 如果我的资源和时间有限 我应该使用什么语言和算法 我包括在限制时间内编写算法的时间长度 想法 编辑 我所说的有限是指在尽可能短的时间内
  • 动态规划的复杂组合条件

    我正在探索动态规划设计方法如何与问题的底层组合属性相关 为此 我正在查看的规范实例硬币找零问题 Let S d 1 d 2 d m and n gt 0是请求的金额 我们可以用多少种方式相加n仅使用中的元素S 如果我们遵循一个动态规划如果要
  • 循环中的递归算法复杂度(运行时间)

    我想了解您对如何检测以下递归算法的 T n 运行时间 的意见 Charm 是一种用于发现事务数据库中频繁闭项集的算法 频繁闭项集列表是在一组交易 tids 中多次出现的频繁项 例如面包和牛奶是经常一起购买的物品 它们是通过将索引为 i 的当
  • 二维空间中的重叠线段

    我需要找出两条线是否相互重叠 如果两条线平行 我有返回 0 的交集代码 但接下来我需要知道这两条平行线是否重叠 Edit A C B D 1号线 A B 2号线 C D 我需要确定第 1 行是否与第 2 行重叠 但两条线的斜率都可以 gt
  • 证明字符串算法[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 计算某个数的某次幂的模(该次幂的数字相当大)

    我想自己计算RSA算法 我需要计算某个数的某个幂的模数 问题是 在一定的功率下 这个数字可能会变得相当大 这就是我想要的 x pow n p q 如何有效地确定 x 如果您使用 NET 4 我建议您查看BigInteger http msd
  • Haskell 中动态规划的高效表

    我已经编码了0 1背包问题 http en wikipedia org wiki Knapsack problem 0 1 knapsack problem在哈斯克尔 我对迄今为止所取得的懒惰和普遍性水平感到相当自豪 我首先提供用于创建和处
  • 数独生成器算法[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我制作了一个生成数独的算法 但效率非常低 每个谜题都需要几分钟才能生成 所以现在我正在尝试以最佳方式重新编写它 但我遇到了一些问题 需
  • GO中的优先级队列

    谁能向我解释一下 我想在GO中实现一个优先级队列 接口实现来自link https golang org pkg container heap example priorityQueue 但优先级最低 我的代码 pq make Priori
  • boost::graph 算法是否能够使用以前的解决方案更快地解决密切相关的新问题?

    我在下图中定义了最大流量问题 最初 所有四个边缘的容量均为 4 个单位 我求从 0 到 3 的最大流量值 答案是 8 沿路径 0 gt 1 gt 3 4 个单位 沿路径 0 gt 2 gt 3 4 个单位 以下代码创建图表并查找最大流量 i
  • 飞船推进AI:控制飞船在x=0、v=0时着陆的力

    我必须编写 AI 代码来控制游戏中宇宙飞船的许多推进喷气机 为简单起见 令空间为一维 宇宙飞船是一个点 只有 1 架喷气机 规则与问题 Let x v and a是飞船的位置 速度 加速度 Let F是施加在船上的喷射力 我知道质量m宇宙飞
  • 动态前缀和

    是否有任何数据结构能够返回数组的前缀和 1 更新元素以及向数组插入 删除元素 所有这些都在 O log n 内 1 前缀和 是从第一个元素到给定索引的所有元素的总和 例如 给定非负整数数组8 1 10 7前三个元素的前缀和是19 8 1 1
  • 获取大于某个数字的元素个数

    我正在尝试解决以下问题 数字被插入到容器中 每次插入数字时 我需要知道容器中有多少元素大于或等于当前插入的数字 我相信这两个操作都可以以对数复杂度完成 我的问题 C 库中有标准容器可以解决这个问题吗 我知道std multiset可以在对数
  • 如何随机打乱地图中的值?

    我有一个 std map 其中键和值均为整数 现在我想随机打乱地图 因此键随机指向不同的值 我尝试了 random shuffle 但它无法编译 请注意 我并没有尝试洗牌键 这对于地图来说没有意义 我正在尝试随机化这些值 我可以将这些值推入
  • 在skiena的书中给出的关于应用dfs在图中查找循环的代码中存在错误

    这是dfs的代码 bool processed MAXV 1 which vertices have been processed bool discovered MAXV 1 which vertices have been found
  • 将矩形分组到网格中

    我有一个随机切片的矩形网格 宽度为 80 单位 我已经将网格每一行的可用空间存储在如下数组中 pX 1 sX 15 pX 30 sX 13 pX 43 sX 1 pX 44 sX 17 pX 1 sX 15 pX 16 sX 14 pX 3
  • 可被 N 整除的最小正数

    1 如何找到能被N整除的最小正数 并且它的各位数字和应该等于N 例如 N 结果 1 1 10 190 并且算法时间不应超过 2 秒 有什么想法 伪代码 pascal c 或 java 吗 设 f len sum mod 为 bool 这意味
  • Mathematica 圆柱分解的计算复杂度是多少

    数学 圆柱分解 http reference wolfram com mathematica ref CylindricalDecomposition html实现一种称为圆柱代数分解的算法 Wolfram MathWorld 的文章圆柱代
  • 如何解决素数函数的大O表示法?

    我正在尝试理解 Big O 表示法 很抱歉 如果我问的问题太明显了 但我似乎无法理解这一点 我有以下 C 代码函数 我正在尝试为其计算 Big O 表示法 for i 2 i lt 100 i for j 2 j lt i j j if i
  • 如何计算某物是否位于某人的视野中

    我有一个对象 它在 2D 空间中具有位置和速度 两者都由向量表示 对象的视野每侧均为 135 度 它看起来与移动的方向相同 速度矢量 我有一些对象 其在 2D 空间中的位置由向量表示 在图中 蓝色背景上的对象是可见的 红色背景上的对象对主体

随机推荐