Java-无序排列数组中特定数量的元素

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

我的问题是:我需要洗牌一个数组,然后只得到前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.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;iif(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;
    }