java.util.Collections.shuffle是否与平台相关?

7
我们需要使用种子来打乱一个ArrayList的顺序。
代码大致如下:
List<String> tempList =  new ArrayList<>()
//code to populdate the tempList
Random rng = new Random(2018);
Collections.shuffle(tempList, rng);

顺便提一下,我们提供静态随机种子的原因是为了确保在洗牌后始终产生相同的结果。

我们观察到,在开发机(Mac)上洗牌的结果与构建机(Linux)上的结果不同。

我想知道这种方法本身是否与平台有关?

JDK 详细信息 Mac 上的:

Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

构建机(我需要更多时间找到更多详细信息,因为我没有访问权限):

jdk1.8.0_162

你使用不同的JVM吗?还是来自Oracle(Hotspot)的官方JVM?哪个版本?您能提供有关测试设置和机器的更多信息吗?Random对象本身呢?尝试使用nextInt生成一些数字,看看序列是否也不同。 - Zabuzard
1
预测性洗牌的目的是什么? - LMC
1
它需要被洗牌,以便列表不按字母顺序排序。它需要可预测的原因是在开发机器和构建机器上使用相同的洗牌顺序。 - DingDong
1
即使Java版本不同,也不应该有任何变化。从文档中可以看到:“为了保证这个属性,特定的算法被指定用于Random类。为了确保Java代码的绝对可移植性,Java实现必须使用此处显示的Random类的所有算法。” - VGR
通常情况下,跨JVM或版本的可移植性往往无法保证。特别是如果您使用的是外部JVM而不是Hotspot。因此,如果可能的话,通常应使用完全相同的JVM和版本。请检查Random是否也生成了不同的序列。如果是,则是洗牌算法的问题。文档明确指出,所有Random的实现必须产生相同的序列。如果没有,则该实现不符合文档要求。 - Zabuzard
显示剩余5条评论
1个回答

0
据我理解,您正在询问此Java程序是否保证在现有的每个Java实现中都打印bcdea
import java.util.Random;
import java.util.ArrayList;
import java.util.Collections;

class Main {
    public static void main(String[] args) {
        Random rng = new Random(42);
        ArrayList<String> list = new ArrayList<String>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        Collections.shuffle(list, rng);
        for (String s : list) System.out.print(s);
    }
};

tio.run 表示,至少在 "OpenJDK 8" 和他们所称的 "JDK" 之间产生相同的输出。但这只是一个非常小而无聊的样本。

官方Oracle文档并不能让人放心:

使用指定的随机源随机排列指定的列表。在假定随机源是公平的情况下,所有排列都以等概率发生。

此实现从最后一个元素向上遍历列表,重复将随机选择的元素交换到“当前位置”。元素是从列表的第一个元素到当前位置(包括当前位置)运行的部分中随机选择的。[强调添加。]

也就是说,Oracle并没有声称每个实现都是这样工作的;而且他们甚至不费心记录交换元素是如何“随机选择”的。

顺便说一下,在看到 Collections.shuffle 被用于 美国投票系统软件 后,我才找到了你的问题,而且正是基于这个假设:无论使用哪个 JDK,它的行为都是可重现的。我同意这一点,但是否真的如此并不清楚。

(顺便说一句,对于 C++ 的 std::shuffle,情况并非如此。在那里,不同的库实现 可以 并且确实 为相同的输入提供不同的洗牌结果。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接