簡體   English   中英

如何最好地比較 Java 整數集?

[英]How best to compare Java sets of integers?

在學習 Java 時,我像許多人一樣使用彩票來提高我的新技能。 作為一個練習,我想為給定的隨機種子創建所有 13.9M 6 個球組合。 我已經成功地為給定的種子生成了行,但我目前沒有檢查它們是否唯一,因此我得到了重復。

我想對他采取什么方法來檢查每個生成的行與先前生成的行有什么建議? 我目前正在使用一個集合來保存每行中的 6 個數字,並且想知道我是否應該比較集合還是應該使用列表或其他東西?

所有建議表示贊賞:-)

您可以使用containsAll()方法。 有生成唯一組合而不是比較所有內容的方法。 請分享一些代碼。

Set<Integer> nums = new HashSet<Integer>(Arrays.asList(new Integer[] {1, 2, 3, 4, 5}));
Set<Integer> nums2 = new HashSet<Integer>(Arrays.asList(new Integer[] {1, 2, 6, 4, 5}));
System.out.println(nums.containsAll(nums2));

有了這些數量的數字,我建議將每行添加到Set中(例如HashSet)。 現在,如果您有自己的代表這些行的類,則必須重寫該類中的equals和hashcode方法。 但是,一旦完成此操作,則在向Set每次添加行之后,都可以使用Set.size()來查看set的大小是否增加。 如果沒有,則為重復項。 這樣,用於檢查重復項的算法實際上比任何其他解決方案都小一個數量級。

(在彩票上讀一點就可以在1到49之間選擇6個數字,並且一個數字不能重復兩次)

如果您希望在執行類似操作之前先確定該組合未被使用,以確保它是唯一的

Set<Set<Integer>> previousCombinations = new HashSet<Set<Integer>>();
...
Set<Integer> newCombination = new HashSet<Integer>(Arrays.asList(22, 10, 1, 14, 45, 14));
if (!previousCombinations.add(newCombination)) {
    // its a duplicate
} else {
    // its not a duplicate
}

但是,另一種不需要檢查以前生成的組合的解決方案如下:

這將生成彩票號碼的隨機組合,這些彩票組合將保證是唯一的,直到用盡所有可能的組合,而無需檢查所有先前的隨機生成的組合。 請注意,樂透號碼以升序返回,因此為了使其更真實,您可以對其進行洗牌。

這可以通過使用

1)一個自定義隨機數生成器,如果您不斷調用getNextValue()方法而沒有重復直到返回所有數字(此后循環),它將以隨機順序生成自定義范圍的所有唯一數字。

2)一種算法,給出一個介於0和組合總數之間的數字,並返回對應於該數字的實際組合。 例如,在我們的情況下,0對應於[0、1、2、3、4、5],13998315對應於[43、44、45、46、47、48]

  public static void main(String[] args) throws Throwable {
    for (int i = 0; i < 1000000; i++) {
      System.out.println(Arrays.toString(getRandomLottoNumbers()));
    }
  }

  private static final RandomFunction lottoFunction = new RandomFunction(choose(49,
      6));

  public static int[] getRandomLottoNumbers() {
    int[] combination = mthCombination(lottoFunction.getNextValue(), 49, 6);
    for (int i = 0; i < combination.length; i++) {
        combination[i]++;
    }
  }

  // Based on http://en.wikipedia.org/wiki/Linear_congruential_generator
  public static final class RandomFunction {

    private final long a;

    private final long c;

    private final long m;

    private int curr = 0;

    public RandomFunction(int period) {
      m = period;
      List<Integer> primes = primeFactors(period);
      Set<Integer> uniquePrimes = new HashSet<Integer>(primes);

      long aMinusOne = 1;
      if (primes.size() >= 2 && primes.get(0) == 2 && primes.get(1) == 2) {
        aMinusOne = 2;
      }

      for (Integer prime : uniquePrimes) {
        aMinusOne *= prime;
      }

      // make 'a' random
      int rand = (int) (1 + (1000 * Math.random()));

      a = (aMinusOne * rand) + 1;

      int potentialC = 0;
      while (potentialC <= 1) {
        potentialC = 2 + (int) (period * Math.random());

        for (Integer prime : uniquePrimes) {
          while (potentialC % prime == 0) {
            potentialC /= prime;
          }
        }
      }
      c = potentialC;
      curr = (int) (period * Math.random());
    }

    public int getNextValue() {
      curr = (int) ((a * curr + c) % m);
      return curr;
    }
  }

  // Based on http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c16255/Linear-Search-based-algorithm-for-Mth-Lexicographic-ordering-of-Mathematical-Permutation-and-Combina.htm
  public static int[] mthCombination(int m, int n, int k) {
    if (k == 0) {
      return new int[0];
    }
    if (k == n) {
      int[] result = new int[k];
      for (int i = result.length - 1; i >= 0; i--) {
        result[i] = --k;
      }
      return result;
    }
    int subChoose = choose(n - 1, k - 1);
    if (m < subChoose) {
      int[] subResult = mthCombination(m, n - 1, k - 1);
      int[] result = new int[subResult.length + 1];
      for (int i = 0; i < subResult.length; i++) {
        result[i + 1] = subResult[i] + 1;
      }
      return result;
    } else {
      int[] result = mthCombination(m - subChoose, n - 1, k);
      for (int i = 0; i < result.length; i++) {
        result[i]++;
      }
      return result;
    }
  }

  public static int choose(int n, int k) {
    if (k < 0 || k > n) {
      return 0;
    }
    if (k > n / 2) {
      k = n - k;
    }

    long denominator = 1;
    long numerator = 1;
    for (int i = 1; i <= k; i++) {
      denominator *= i;
      numerator *= (n + 1 - i);
    }
    return (int) (numerator / denominator);
  }

  public static List<Integer> primeFactors(int number) {
    List<Integer> primeFactors = new ArrayList<Integer>();
    if (number < 1) {    
      return primeFactors;
    }

    while (number % 2 == 0) {
      primeFactors.add(2);
      number /= 2;
    }
    for (int i = 2; i * i <= number; i += 2) {
      while (number % i == 0) {
        primeFactors.add(i);
        number /= i;
      }
    }
    if (number != 1) {
      primeFactors.add(number);
    }

    return primeFactors;
  }

您可以簡單地使用 Set 接口的 equals 方法:

Set<Integer> myFirstSetOfIntegers = Set.of(1,3,17);
Set<Integer> mySecondSetOfIntegers = Set.of(1,3,17);
myFirstSetOfIntegers.equals(mySecondSetOfIntegers)

檢查 Set.equals 的 javadoc 描述。 它完全符合您的要求:

* Compares the specified object with this set for equality.  Returns
* {@code true} if the specified object is also a set, the two sets
* have the same size, and every member of the specified set is
* contained in this set (or equivalently, every member of this set is
* contained in the specified set).  This definition ensures that the
* equals method works properly across different implementations of the
* set interface.

我還建議研究其他數據結構,以防您處理組合復雜的算法。 例如,您可以用 BitSet 表示每個 6 球組合。 對於您的用例,BitSet::equals 應該比 Set::equals 快得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM