Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 算法太慢,无法洗牌ArrayList_Java_Algorithm_Arraylist_Shuffle - Fatal编程技术网

Java 算法太慢,无法洗牌ArrayList

Java 算法太慢,无法洗牌ArrayList,java,algorithm,arraylist,shuffle,Java,Algorithm,Arraylist,Shuffle,我试图在java上实现Fisher-Yates shuffle算法。它可以工作,但是当我的ArrayList的大小>100000时,它运行得非常慢。我将向您展示我的代码,您是否看到优化代码的方法?我对ArrayList中.get和.set的复杂性做了一些研究,O(1)对我来说是有意义的 更新1:我注意到我的实现是错误的。这是正确的Fisher-Yates算法。此外,我还包括了我的next()函数,以便大家可以看到它。我用java.Random测试了我的next()函数是否有问题,但它给出了相同的

我试图在java上实现Fisher-Yates shuffle算法。它可以工作,但是当我的ArrayList的大小>100000时,它运行得非常慢。我将向您展示我的代码,您是否看到优化代码的方法?我对ArrayList中.get和.set的复杂性做了一些研究,O(1)对我来说是有意义的

更新1:我注意到我的实现是错误的。这是正确的Fisher-Yates算法。此外,我还包括了我的
next()
函数,以便大家可以看到它。我用java.Random测试了我的
next()
函数是否有问题,但它给出了相同的结果。我相信问题在于我的数据结构的使用

更新2:我做了一个测试,ArrayList是RandomAccess的一个实例。所以问题不在这里

private long next(){ // MurmurHash3

    seed ^= seed >> 33;
    seed *= 0xff51afd7ed558ccdL;
    seed ^= seed >> 33;
    seed *= 0xc4ceb9fe1a85ec53L;
    seed ^= seed >> 33;

    return seed;

}


public int next(int range){

    return (int) Math.abs((next() % range));

}

public ArrayList<Integer> shuffle(ArrayList<Integer> pList){

    Integer temp;
    int index;
    int size = pList.size();

    for (int i = size - 1; i > 0; i--){

        index = next(i + 1);
        temp = pList.get(index);
        pList.set(index, pList.get(i));
        pList.set(i, temp);

    }

    return pList;

}
private long next(){//hash3
种子^=种子>>33;
种子*=0xff51afd7ed558ccdL;
种子^=种子>>33;
种子*=0xc4ceb9fe1a85ec53L;
种子^=种子>>33;
返回种子;
}
公共整数下一个(整数范围){
return(int)Math.abs((next()%range));
}
公共ArrayList shuffle(ArrayList pList){
整数温度;
整数指数;
int size=pList.size();
对于(int i=size-1;i>0;i--){
索引=下一个(i+1);
temp=pList.get(索引);
pList.set(索引,pList.get(i));
第一组(i,温度);
}
返回层;
}
(更适合代码审查论坛。)

我改变了我能做的:

Random random = new Random(42);
for (ListIterator<Integer>.iter = pList.listIterator(); iter.hasNext(); ) {
    Integer value = iter.next();
    int index = random.nextInt(size);
    iter.set(pList.get(index));
    pList.set(index, value);
}
Random Random=new Random(42);
for(ListIterator.iter=pList.ListIterator();iter.hasNext();){
整数值=iter.next();
int index=random.nextInt(大小);
iter.set(pList.get(index));
pList.set(索引、值);
}
由于ArrayList是大型数组的列表,因此可以在ArrayList构造函数中设置initialCapacity
trimToSize()
可能也会起作用。使用ListIterator意味着当前部分数组中已经存在一个ListIterator,这可能会有所帮助


随机构造函数的可选参数(此处42)允许选择一个固定的随机序列(=可重复),允许在开发计时和跟踪相同的序列。

尝试此代码,并将执行时间与fisher-yates方法进行比较。 这可能是“下一个”速度较慢的方法

function fisherYates(array) {
     for (var i = array.length - 1; i > 0; i--) {
     var index = Math.floor(Math.random() * i);
     //swap
     var tmp = array[index];
     array[index] = array[i];
     array[i] = tmp;
}

编辑:在正确执行算法后添加了一些注释

Fisher-Yates算法依赖于均匀分布的随机整数来产生无偏排列。使用散列函数(hash3)生成随机数,并引入abs和模运算以强制数字在固定范围内,这使得实现的健壮性降低

此实施使用PRNG,应能满足您的需要:

public <T> List<T> shuffle(List<T> list) {

   // trust the default constructor which sets the seed to a value very likely
   // to be distinct from any other invocation of this constructor
   final Random random = new Random();

   final int size = list.size();

   for (int i = size - 1; i > 0; i--) {
      // pick a random number between one and the number
      // of unstruck numbers remaining (inclusive)
      int index = random.nextInt(i + 1);
      list.set(index, list.set(i, list.get(index)));
   }

   return list;

}

结合一些分散在评论和其他答案中的片段:

  • 最初的代码不是的实现。这只是交换随机元素。这意味着某些排列比其他排列更有可能,并且结果不是真正随机的
  • 如果存在瓶颈,它可能(基于提供的代码)只存在于
    next
    方法中,而您对此没有做任何说明。它应该被
    java.util.Random
下面是一个它可能看起来像什么的例子。(请注意,
speedTest
方法甚至不打算远程用作“基准测试”,但应仅表明即使对于大型列表,执行时间也可以忽略不计)

import java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入java.util.Random;
鱼翅类{
公共静态void main(字符串[]args){
基本测试();
速度测试();
}
私有静态void基本测试(){
List List=newarraylist(Arrays.asList(1,2,3,4,5));
洗牌(列表,新随机(0));;
系统输出打印项次(列表);
}
专用静态无效速度测试(){
列表=新的ArrayList();
int n=1000000;
对于(int i=0;i 0;i--){
int index=random.nextInt(i+1);
T=list.get(索引);
set(索引,list.get(i));
列表集(i,t);
}
}
}

旁白:您提供了一个列表作为参数,并返回了相同的列表。这在某些情况下可能是合适的,但在这里没有任何意义。对于这种方法的签名和行为,有几个选项。但最有可能的是,它应该收到一个
列表
,并将该列表重新排列到位。事实上,显式检查列表是否实现了接口也是有意义的。对于未实现
随机访问
接口的
列表
,此算法将降级为二次性能。在这种情况下,最好将给定的列表复制到一个实现了
随机访问的列表中,将此副本洗牌,然后将结果复制回原始列表中

所以下次只需使用“编辑”;D并且请给我们看下一个()方法,因为它可能也是瓶颈。为下一个()方法显示代码…这可能就是花费这么长时间的原因。水晶球:在下一个
中是否创建了
随机
实例?为什么要使用
列表
而不是,比如说
int[]
?您正在计算pList.size()每次循环迭代时,改用size变量,除此之外,如前所述,如果在下一个函数中实例化了随机类,则瓶颈可能在下一个函数中。将它拉到迭代循环之外,并将实例作为引用变量传递给nextAn:这也不是Fisher-Yates Shuffle。但至少显示了java.uti.Random的正确用法。。。
public void testShuffle() {
   List<Integer> list = new ArrayList<>();

   for (int i = 0; i < 1_000_000; i++) {
      list.add(i);
   }

   System.out.println("size: " + list.size());

   System.out.println("Fisher-Yates shuffle");
   for (int i = 0; i < 10; i++) {
      long start = System.currentTimeMillis();
      shuffle(list);
      long stop = System.currentTimeMillis();
      System.out.println("#" + i + " " + (stop - start) + "ms");
   }

   System.out.println("Java shuffle");
   for (int i = 0; i < 10; i++) {
      long start = System.currentTimeMillis();
      Collections.shuffle(list);
      long stop = System.currentTimeMillis();
      System.out.println("#" + i + " " + (stop - start) + "ms");
   }
}
size: 1000000
Fisher-Yates shuffle
#0 84ms
#1 60ms
#2 42ms
#3 45ms
#4 47ms
#5 46ms
#6 52ms
#7 49ms
#8 47ms
#9 53ms
Java shuffle
#0 60ms
#1 46ms
#2 44ms
#3 48ms
#4 50ms
#5 46ms
#6 46ms
#7 49ms
#8 50ms
#9 47ms
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

class FisherYatesShuffle {
    public static void main(String[] args) {
        basicTest();
        speedTest();
    }

    private static void basicTest() {
        List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
        shuffle(list, new Random(0));;
        System.out.println(list);
    }

    private static void speedTest() {
        List<Integer> list = new ArrayList<Integer>();
        int n = 1000000;
        for (int i=0; i<n; i++) {
            list.add(i);
        }
        long before = System.nanoTime();
        shuffle(list, new Random(0));;
        long after = System.nanoTime();
        System.out.println("Duration "+(after-before)/1e6+"ms");
        System.out.println(list.get(0));
    }

    public static <T> void shuffle(List<T> list, Random random) {
        for (int i = list.size() - 1; i > 0; i--) {
            int index = random.nextInt(i + 1);
            T t = list.get(index);
            list.set(index, list.get(i));
            list.set(i, t);
        }
    }
}