Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.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 从一个原始整数列表生成洗牌整数列表的算法_Java_Arrays_Algorithm_Math_Statistics - Fatal编程技术网

Java 从一个原始整数列表生成洗牌整数列表的算法

Java 从一个原始整数列表生成洗牌整数列表的算法,java,arrays,algorithm,math,statistics,Java,Arrays,Algorithm,Math,Statistics,有一个xuniqueIntegers的数组列表,我需要将它们在yz大小的数组列表之间随机分布。请记住: x y z是可变值 数字不能在结果数组上重复 结果列表不能包含相同的数字!(它们的顺序必须不同) 如果计算结果数组中的出现次数,则原始数组中的每个数必须尽可能使用与其他数相同的次数 必须使用原始数组的所有数字,而不是 它们不能不被使用 如果可能,必须在Java7中工作。不是100%强制,但 结果组合将用于类似于彩票的东西,因此它们不能太连续,必须非常随机。此外,它们将按从最小到最大的顺序进行

有一个
x
unique
Integers
的数组列表,我需要将它们在
y
z大小的数组列表之间随机分布。请记住:

  • x y z
    是可变值
  • 数字不能在结果数组上重复
  • 结果列表不能包含相同的数字!(它们的顺序必须不同)
  • 如果计算结果数组中的出现次数,则原始数组中的每个数必须尽可能使用与其他数相同的次数
  • 必须使用原始数组的所有数字,而不是 它们不能不被使用
  • 如果可能,必须在Java7中工作。不是100%强制,但
  • 结果组合将用于类似于彩票的东西,因此它们不能太连续,必须非常随机。此外,它们将按从最小到最大的顺序进行排序
  • 最初,我尝试生成所有可能的组合,目的是获得所需的数量,但这是不可行的,因为如果在11的组合中选择40个数字这样的高值,会有数百万个数字,CPU会在很多时间内无法计算,因此,我尝试开发一个更简单的算法,而不计算所有的组合(我在下面发布代码)
一个示例是,当您有一个由8个元素组成的数组的原点,并且希望输出3个大小为6的数组时:

原始阵列列表:[1,2,3,4,5,6,7,8]

结果输出:[7,5,3,6,4,8],[7,5,1,8,2,3],[8,1,2,3,4,6]

我开发了一个算法,并在注释中进行了解释。首先,我创建了一个包含总位置的数组,并计算每个数字必须重复多少次才能填充输出数组。然后我用每个重复了必要次数的数字填充数组,如果数组没有满(因为当我除法得到
placesByNumber
时,我将四舍五入为整数),我将用原始数字集中的随机数填充数组。在那之后我洗牌数字,最后在那之后我填充结果数组,记住我不能在每个结果数组中重复数字

有时,问题出现在这里,我得到的情况是,最后一个数组没有完全填充,因为被洗牌的
numbersGroup
变量的最后一个数字包含在最后一个数组中

这是一个故障示例:

原始阵列列表:[1,2,3,4,5,6,7,8]

用于填充结果数组的随机数字组:

[8,2,4,4,5,7,2,3,8,2,1,5,7,1,6,3,6,1]

结果数组:(第三个没有6个元素,因为6和1是 (已包含在其上)

[8,2,4,5,7,3],[4,2,8,1,5,7],[2,1,6,3]]

我找到了一些非常难看的方法来解决这个问题,但是这些方法效率很低,我正试图找到一个更好、更有效的算法来实现这个目标

这是我的源代码:

public static List<List<Integer>> getOptimizedCombinations(List<Integer> numbers, int numbersPerCombination, int desiredCombinations){
    List<List<Integer>> result = new ArrayList<>();
    
    //calculate total places and how many places correspond to each number.
    int totalPlaces = numbersPerCombination * desiredCombinations;
    int placesByNumber = totalPlaces / numbers.size();
    
    //instantiating array with the total number of places
    Integer[] numbersGroup = new Integer[totalPlaces];
    
    //filling the array with the numbers, now we know how many times a number must be inside the array, 
    //so we put the numbers. First we do it in order, later we will shuffle the array.
    int pos = 0;
    for (int n : numbers) {
        for (int i=0; i<placesByNumber; i++) {
            numbersGroup[pos] = n;
            pos++;
        }
    }
    
    //if there are places for fill, we fill it with random numbers. This can be possible because when we divide the total places between the 
    //numbers size, it can give a decimal as a result, and we round it to lower binary number without decimals, so it is possible to
    //have non filled places.       
    if (pos<totalPlaces) {
        while(pos<totalPlaces) {                
            numbersGroup[pos] = numbers.get(getRandom(0, numbers.size()));
            pos++;              
        }
    }       
    
    shuffleArray(numbersGroup);
    
    //we instantiate the arraylists
    for (int i=0; i<desiredCombinations; i++) {
        result.add(new ArrayList<Integer>());
    }
                    
    //filling the arraylists with the suffled numbers
    for (int i=0; i<numbersGroup.length; i++) {
        for (int j=0; j<result.size(); j++) {
            //if the combination doesn't have the number and the combination is not full, we add the number
            if (!result.get(j).contains(numbersGroup[i]) && result.get(j).size()<numbersPerCombination) {
                result.get(j).add(numbersGroup[i]);
                break;
            }
        }
    }
    
    return result;
}

static void shuffleArray(Integer[] ar){
    Random rnd = new Random();
    for (int i = ar.length - 1; i > 0; i--)
    {
        int index = rnd.nextInt(i + 1);
        // Simple swap
        int a = ar[index];
        ar[index] = ar[i];
        ar[i] = a;
    }
}

public static int getRandom(int min, int max) {
    return (int)(Math.random() * max + min);
}
公共静态列表GetOptimizedCompositions(列表编号、整数PerComposition、整数DesiredCompositions){
列表结果=新建ArrayList();
//计算总学额以及每个数字对应的学额。
int totalPlaces=数字组合*所需组合;
int placesByNumber=totalPlaces/numbers.size();
//正在使用总位置数实例化数组
整数[]numbersGroup=新整数[totalPlaces];
//用数字填充数组,现在我们知道一个数字必须在数组中出现多少次,
//所以我们把数字放进去。首先我们按顺序做,然后我们将洗牌数组。
int pos=0;
用于(整数n:数字){

对于(int i=0;i您可以使用
Stream
s将无序列表限制为
z
元素:

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);

List<List<Integer>> result = new LinkedList<>();
for(int i = 0; i < y; i++) {
  Collections.shuffle(numbers);
  List<Integer> list = numbers.stream().limit(z).collect(Collectors.toList());
  result.add(list);
}

System.out.println(result);
想法 为了让这一切顺利进行,我们需要

  • z
    (每个新列表的长度<输入列表的长度),或者我们无法在没有重复项的情况下填充新列表
  • y·z
    (列表数量·列表长度)必须是
    x
    的倍数,否则某些数字必须比其他数字更频繁地出现
我们的想法是

  • 洗牌输入列表
  • 重复输入列表,这样我们就可以得到
    y·z
    数字。这可以在不重复列表的情况下完成。诀窍是使用模
    %
    运算符
  • 将重复输入列表均匀地分成长度为
    z
    y
    列表
  • 洗牌每个新列表
  • 输入

    洗牌

    3 5 8 6 7 2 4 1
    
    重复

    3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1 3 5 8 6 7 2 4 1
    
    分裂

    洗牌每个列表

    7 3 5 6 2 8    1 3 4 8 6 5    3 4 1 5 7 2    2 7 4 1 8 6
    
    洗牌列表

    1 3 4 8 6 5    2 7 4 1 8 6    7 3 5 6 2 8    3 4 1 5 7 2
    
    节目 这个程序应该在Java7中工作,但是我只用Java11测试了它

    import java.util.*;
    public class Shuffle {
        public static void main(String[] args) {
            System.out.println(splitShuffle(Arrays.asList(1,2,3,4,5,6,7,8), 6, 3));
        }
        public static List<List<Integer>> splitShuffle(
                List<Integer> input, int newLength, int listCount) {        
            assert newLength * listCount % input.size() == 0 : "Cannot distribute numbers evenly";
            input = new ArrayList<>(input);
            Collections.shuffle(input);
            List<List<Integer>> result = new ArrayList<>(listCount);
            for (int i = 0; i < listCount; ++i) {
                result.add(rotatingCopy(input, i * newLength, newLength));
            }
            Collections.shuffle(result);
            return result;
        }
        private static List<Integer> rotatingCopy(List<Integer> input, int startIndex, int length) {
            assert length < input.size() : "copy would have to contain duplicates";
            List<Integer> copy = new ArrayList<>(length);
            for (int i = 0; i < length; ++i) {
                copy.add(input.get((startIndex + i) % input.size()));
            }
            Collections.shuffle(copy);
            return copy;
        }
    }
    
    正如我们所看到的,每个数字正好出现两次,每个子列表只有唯一的数字

    完整性 至少对于输入列表
    [1,2,3]
    y=3,z=2
    ,我可以验证是否可以生成所有可能的48个输出。我知道使用以下bash命令有48个组合:

    printf %s\\n {1..3}{1..3},{1..3}{1..3},{1..3}{1..3} | grep -Pv '(\d)\1' |
    tr -d , | awk '{print $1, gsub(1,""), gsub(2,""), gsub(3,"")}' |
    grep -F ' 2 2 2' | cut -d' ' -f1 | sort -u | wc -l
    

    我的方法是洗牌原始列表,然后不断迭代,直到填充目标列表,然后洗牌每个目标列表。这将保持每个数字的出现平衡。如果
    numbersporcombinion
    numbers.size()
    ,它也会起作用

    公共类公平列表{
    公共静态void main(字符串[]args){
    列表编号=数组.asList(1,2,3,4,5,6,7,8);
    List FairList=GetOptimizedCompositions(数字6,3);
    System.out.println(公平列表);
    }
    公共静态列表GetOptimizedCompositions(列表编号、整数PerComposition、整数DesiredCompositions){
    列表源=新的ArrayList(数字);
    收藏。洗牌(来源);
    List fairNumbersLists=新的ArrayList(desiredCombinat
    
    7 3 5 6 2 8    1 3 4 8 6 5    3 4 1 5 7 2    2 7 4 1 8 6
    
    1 3 4 8 6 5    2 7 4 1 8 6    7 3 5 6 2 8    3 4 1 5 7 2
    
    import java.util.*;
    public class Shuffle {
        public static void main(String[] args) {
            System.out.println(splitShuffle(Arrays.asList(1,2,3,4,5,6,7,8), 6, 3));
        }
        public static List<List<Integer>> splitShuffle(
                List<Integer> input, int newLength, int listCount) {        
            assert newLength * listCount % input.size() == 0 : "Cannot distribute numbers evenly";
            input = new ArrayList<>(input);
            Collections.shuffle(input);
            List<List<Integer>> result = new ArrayList<>(listCount);
            for (int i = 0; i < listCount; ++i) {
                result.add(rotatingCopy(input, i * newLength, newLength));
            }
            Collections.shuffle(result);
            return result;
        }
        private static List<Integer> rotatingCopy(List<Integer> input, int startIndex, int length) {
            assert length < input.size() : "copy would have to contain duplicates";
            List<Integer> copy = new ArrayList<>(length);
            for (int i = 0; i < length; ++i) {
                copy.add(input.get((startIndex + i) % input.size()));
            }
            Collections.shuffle(copy);
            return copy;
        }
    }
    
    [[2, 6, 7, 8, 1, 3], [4, 3, 7, 5, 2, 8], [1, 2, 6, 5, 4, 8]]
    [[2, 7, 5, 4, 6, 1], [4, 7, 2, 6, 8, 3], [1, 3, 5, 8, 6, 4]]
    [[4, 1, 2, 5, 6, 3], [5, 3, 8, 4, 6, 7], [5, 1, 2, 7, 3, 8]]
    [[5, 3, 8, 2, 6, 4], [1, 7, 4, 5, 6, 3], [1, 6, 2, 8, 7, 4]]
    
    printf %s\\n {1..3}{1..3},{1..3}{1..3},{1..3}{1..3} | grep -Pv '(\d)\1' |
    tr -d , | awk '{print $1, gsub(1,""), gsub(2,""), gsub(3,"")}' |
    grep -F ' 2 2 2' | cut -d' ' -f1 | sort -u | wc -l
    
    public class FairLists {
    
        public static void main(String[] args) {
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
            List<List<Integer>> fairLists = getOptimizedCombinations(numbers, 6, 3);
            System.out.println(fairLists);
        }
    
        public static List<List<Integer>> getOptimizedCombinations(List<Integer> numbers, int numbersPerCombination, int desiredCombinations){
            List<Integer> source = new ArrayList<>(numbers);
            Collections.shuffle(source);
    
            List<List<Integer>> fairNumbersLists = new ArrayList<>(desiredCombinations);
    
            int sourceIndex = 0;
            while (desiredCombinations > 0) {
    
                List<Integer> fairNumbers = new ArrayList<>(numbersPerCombination);
                for (int i = 0; i < numbersPerCombination; i++) {
                    fairNumbers.add(source.get(sourceIndex));
                    sourceIndex++;
                    if (sourceIndex == source.size()) {
                        sourceIndex = 0;
                    }
                }
    
                Collections.shuffle(fairNumbers);
                fairNumbersLists.add(fairNumbers);
    
                desiredCombinations--;
            }
    
            Collections.shuffle(fairNumbersLists);
            return fairNumbersLists;
        }
    }
    
    public class Combination {
    
        private Combination() {
        }
    
        /**
         *
         * @param n:
         *            n-set
         * @param m:
         *            m-subset
         * @return number of combinations C(n, m) = (n(n - 1)...(n - m + 1)) / m!
         */
        public static BigInteger C(int n, int m) {
            if (m > n) {
                return BigInteger.ZERO;
            } else {
                if ((n - m) > m) {
                    return C(n, (n - m));
                }
            }
            BigInteger numerator = BigInteger.ONE;
            BigInteger denominator = BigInteger.ONE;
    
            for (int i = n; i > m; i--) {
                numerator = numerator.multiply(BigInteger.valueOf(i));
            }
    
            for (int i = (n - m); i > 1; i--) {
                denominator = denominator.multiply(BigInteger.valueOf(i));
            }
    
            return numerator.divide(denominator);
        }
    
        /**
         *
         * @param <T>
         *            Type
         * @param elements
         *            List of elements to combine
         * @param numberOfRequiredElements
         *            must be less or equal to elements.size()
         * @param combinatios
         *            result: List&lt;List&lt;T&gt;&gt; of all combinations
         * @param temp
         *            used for recursive purposes
         * @return combinations<br>
         * 
         *         Example of usage:<br>
         *         List&lt;Integer&gt; elements = new ArrayList&lt;&gt;();<br>
         *         for (int i = 1; i &lt;= 7; i++) {<br>
         *         &emsp;elements.add(i);<br>
         *         }<br>
         *         List&lt;Integer&gt; temp = new ArrayList&lt;&gt;();<br>
         *         List&lt;List&lt;Integer&gt;&gt; combinations = new
         *         ArrayList&lt;&gt;();<br>
         *         System.out.println(Combination.allCombinations(elements, 6,
         *         combinations, temp));<br>
         *
         */
        public static <T> List<List<T>> allCombinations(List<T> elements, int numberOfRequiredElements,
                List<List<T>> combinatios, List<T> temp) {
            if (numberOfRequiredElements == 0) {
                // System.out.print(temp);
                combinatios.add(new ArrayList<>(temp));
            } else {
                for (int i = 0; i < elements.size(); i++) {
                    temp.add(elements.get(i));
                    List<T> subList = elements.subList(i + 1, elements.size());
                    allCombinations(subList, numberOfRequiredElements - 1, combinatios, temp);
                    temp.remove(temp.size() - 1);
                }
            }
            return combinatios;
        }
    
        /**
         *
         * @param args
         *            Not required for this purpose
         */
        public static void main(String[] args) {
            int NO_OF_ELEMENS = 10;
            int REQURED_COMBINATION_SIZE = 6;
    
            List<Integer> elements = new ArrayList<>();
            for (int i = 1; i <= NO_OF_ELEMENS; i++) {
                elements.add(i);
            }
            System.out.println("This is an example of using methods in this class\n");
            System.out.println("Elements are " + elements + " (size = " + elements.size() + ")");
            System.out.println("Requred size of combination is " + REQURED_COMBINATION_SIZE);
            System.out.println("Number of all combinations is " + Combination.C(NO_OF_ELEMENS, REQURED_COMBINATION_SIZE));
            List<Integer> temp = new ArrayList<>();
            List<List<Integer>> combinations = new ArrayList<>();
            System.out.println("All combinations are:");
            Combination.allCombinations(elements, REQURED_COMBINATION_SIZE, combinations, temp);
            int i = 0;
            for (List<Integer> combination : combinations) {
                System.out.println(++i + "\t" + combination);
            }
        }
    }
    
    public static long noOfCombinations(int n, int m){
    
        //this part is for fewer multiplications
        //b/c 4 out of 6 has the same number of combinations as 2 out of 6
        if (m > n) {
            return 0;
        } else {
            if ((n - m) > m) {
                return noOfCombinations(n, (n - m));
            }
        }
    
        long numerator = 1;
        long denominator = 1;
    
        //these two loops are for partial factorial
        for (int i = n; i > m; i--) {
            numerator *= i;
        }
    
        for (int i = (n - m); i > 1; i--) {
            denominator *= i;
        }
    
        // no of combinations
        return numerator / denominator;
    }