Java-无序排列数组中特定数量的元素
我的问题是:我需要洗牌一个数组,然后只得到前N个元素 我目前正在洗牌整个阵列,其中有50+个元素,但这给我带来了性能问题,因为洗牌例程被称为1e+9次 我目前正在实施Fisher-Yates算法来洗牌:Java-无序排列数组中特定数量的元素,java,arrays,algorithm,shuffle,Java,Arrays,Algorithm,Shuffle,我的问题是:我需要洗牌一个数组,然后只得到前N个元素 我目前正在洗牌整个阵列,其中有50+个元素,但这给我带来了性能问题,因为洗牌例程被称为1e+9次 我目前正在实施Fisher-Yates算法来洗牌: public static void shuffle(int[] array) { Random gen = new Random(); for (int i = array.length - 1; i > 0; i--) { int index = gen
public static void shuffle(int[] array) {
Random gen = new Random();
for (int i = array.length - 1; i > 0; i--) {
int index = gen.nextInt(i + 1);
int a = array[index];
array[index] = array[i];
array[i] = a;
}
}
然后我只选择前N个元素
我也尝试过使用,但它只节省了我1秒钟。这还不够,因为我的程序运行30秒。此外,我可能没有正确地实现它,因为与Fisher-Yates算法相比,我没有得到相同的结果。这是我的实现:
public static int[] shuffle(int[] array, int N) {
int[] ret = new int[N];
for (int i = 0; i < N; i++) {
ret[i] = array[i];
}
Random gen = new Random();
int j;
for (int i = N; i < array.length; i++) {
j = gen.nextInt(i+1);
if (j <= N - 1)
ret[j] = array[i];
}
return ret;
}
公共静态int[]洗牌(int[]数组,int N){
int[]ret=新的int[N];
对于(int i=0;i if(j从数组中获取N
无序元素的简单方法如下:
选择一个随机元素r
将元素r
添加到输出中
将数组的最后一个元素移动到r
位置,并将数组大小缩小1
重复N
次
代码:
public static int[] shuffle(int[] array, int N) {
int[] result = new int[N];
int length = array.length;
Random gen = new Random();
for (int i = 0; i < N; i++) {
int r = gen.nextInt(length);
result[i] = array[r];
array[r] = array[length-1];
length--;
}
return result;
}
公共静态int[]洗牌(int[]数组,int N){
int[]结果=新的int[N];
int length=array.length;
Random gen=新的Random();
对于(int i=0;i
与FY相比,该算法的优势在于它只计算洗牌数组的第一个N
元素,而不是洗牌整个数组
您的优化算法不是最优的,原因有两个:
第一个N
元素永远不会被洗牌。例如,元素0永远不会出现在洗牌数组中的位置1
您仍在做大量工作。如果N=10
且总数组长度为1000000
,您仍在计算大约1000000
随机值,而您只需要10
如果存在以下情况,请勿尝试重新发明车轮:
publicstaticint[]shuffle(int[]array,intn){
列表=新的ArrayList();
for(int r:array){
列表。添加(r);
}
集合。洗牌(列表);
整数[]ret=list.toArray();//整数为int,自动装箱除外
返回ret;
}
您可以通过只进行N次迭代来修改FY,然后获取数组的最后N个元素。或者,在数组的开头而不是结尾构建无序区域,并获取前N个元素。但是,这将只保存实际无序的迭代,并且其中的增益将是N/50。这可能不是e不
根据具体情况,您可能会生成一百万个洗牌组,然后在每个1e9外部迭代中随机选择一个。这将在每个外部迭代中生成一个随机数,再加上当前洗牌调用的千分之一。虽然我同意这种方法可行,但我认为这是一个很好的答案s的问题应该解释为什么这是OP建议的更好的方法。@Duncan我同意。我很难理解OP的优化算法。我验证了它的正确性(除了复制第一个N
元素而不洗牌),但效率不高。你为什么要重新发明轮子?@hd1问题不是洗牌数组,而是得到一个长度为N
集合的随机子列表。洗牌不能做到这一点。@Heuster你的解决方案工作得很好。将元素放在数组末尾的想法非常好。@hd1强化了Heu斯特尔说,这不是重新发明轮子,因为收集。shuffle
不起作用,通常的shuffle方法也不起作用。可能的重复我不认为你的问题实际上是关于洗牌-看起来你想要的是一种快速方法,从一组M个项目中随机挑选N个项目,其中NN的数组中选取N个随机元素。请参阅严格的O(N)
(时间和空间上的)算法如果数组的长度>>N,那么你的解决方案很耗时,这是OP不想要的。你的解决方案看起来不错。虽然我没有尝试过这两种方法,但我认为它们都可以。我没有选择你的答案,因为Heuster给出的答案很适合我的代码。
public static int[] shuffle(int[] array, int N) {
List<Integer> list = new ArrayList<Integer>();
for (int r : array) {
list.add(r);
}
Collections.shuffle(list);
Integer[] ret = list.toArray(); // Integer is int, except autoboxed
return ret;
}