[英]How to retrieve the original string from the shuffled string of a Java algorithm
[英]Algorithm for generating lists of shuffled Integers from one original list of Integers
有x
唯一Integers
的 ArrayList ,我需要將它們分布在z
大小的y
ArrayLists 之間。 請記住:
xyz
是變量值。一個示例是這樣的,當您有一個由 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);
}
這種方式稱為:
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);
您可以使用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]]
為了使這項工作,我們需要
z < x
(每個新列表的長度 < 輸入列表的長度)或者我們無法在沒有重復的情況下填充新列表。y·z
(列表數·列表長度)必須是x
的倍數,否則某些數字必須比其他數字出現得更頻繁。這個想法是
y·z
數字。 這可以在不實際重復列表的情況下完成。 訣竅是使用模%
運算符。z
y
列表。輸入
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
打亂列表列表
1 3 4 8 6 5 2 7 4 1 8 6 7 3 5 6 2 8 3 4 1 5 7 2
這個程序應該可以在 Java 7 中運行。但是我只用 Java 11 測試過它。
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]
和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
我的方法是對原始列表進行混洗,然后不斷迭代直到填充目標列表,然后對每個目標列表進行混洗。 這將使每個數字的出現保持平衡。 它也適用於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;
}
}
在我們的聊天中,您讓我編寫有關組合的代碼,所以這不是您問題的答案,只是您可以探索的代碼:
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<List<T>> of all combinations
* @param temp
* used for recursive purposes
* @return combinations<br>
*
* Example of usage:<br>
* List<Integer> elements = new ArrayList<>();<br>
* for (int i = 1; i <= 7; i++) {<br>
*  elements.add(i);<br>
* }<br>
* List<Integer> temp = new ArrayList<>();<br>
* List<List<Integer>> combinations = new
* ArrayList<>();<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);
}
}
}
我希望這段代碼能有所幫助。 PS 對無法閱讀的評論感到抱歉 - 我之前在 NetBeans 中寫過這個,現在我正在使用 IntelliJ ...
編輯:使用 long 而不是BigInteger
來計算組合數......(就個人而言,我不推薦這樣做)。
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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.