从一个原始整数列表中生成混洗的整数列表的算法

时间:2018-11-06 15:22:25

标签: java arrays algorithm math statistics

具有x个唯一的Integers的ArrayList,我需要在y个大小的z个ArrayList之间重新分配它们。请记住:

  • x y z是变量值。
  • 数字不能在结果数组上重复。
  • 结果列表中不能包含相同的数字! (命令它们必须不同)
  • 如果要计算结果数组中的出现次数,则原始数组的每个编号必须与其他数组尽量使用相同的次数。
  • 必须使用原始数组的所有数字,没有一个 它们不能不被使用。
  • 如果可能,必须在Java 7中工作。不是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,6,3,6,1]

     

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

     

[[8,2,4,5,7,3],[4,2,8,1,5,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);
}

这种方式:

    ArrayList<Integer> numbers = new ArrayList<Integer>() {{ 
        add(1); 
        add(2);
        add(3); 
        add(4); 
        add(5); 
        add(6); 
        add(7);
        add(8);
    }};
    getOptimizedCombinations(numbers, 6, 3);

4 个答案:

答案 0 :(得分:1)

您可以使用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);

也许可以用一种更优雅的方式来完成,但是输出应该是这样的:

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

答案 1 :(得分:1)

想法

要使它起作用,我们需要

  • z < x(每个新列表的长度<输入列表的长度),否则我们无法在没有重复项的情况下填写新列表。
  • y·z(列表数·列表长度)必须是x的倍数,否则某些数字的出现频率必须高于其他数字。

想法是

  1. 随机播放输入列表。
  2. 重复输入列表,使我们最终得到y·z个数字。无需实际重复此列表即可完成此操作。诀窍是使用模%运算符。
  3. 甚至将重复的输入列表分成长度为y的{​​{1}}个列表。
  4. 随机播放每个新列表。

输入

z

随机播放

1 2 3 4 5 6 7 8

重复

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

随机播放每个列表

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

程序

该程序应在Java 7中运行。但是,我只在Java 11中对其进行过测试。

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]] [1, 2, 3],我可以验证是否可以生成所有可能的48个输出。我知道使用以下bash命令有48种组合:

y=3, z=2

答案 2 :(得分:0)

我的方法是混洗原始List,然后连续迭代直到填充目标List,然后混洗每个目标List。这将使每个数字的出现保持平衡。如果numbersPerCombination> numbers.size()也可以使用。

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;
    }
}

答案 3 :(得分:0)

在聊天过程中,您要求我编写有关组合的代码,这对您的问题没有帮助,只是您可以了解的代码

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>

我希望这段代码对您有所帮助。 附言抱歉,注释不可读-我之前在NetBeans中编写了此注释,现在使用的是IntelliJ ...

编辑:使用长号而不是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); } } } 来计算组合数量...(个人,我不建议这样做)。

BigInteger