【问题标题】:Integer partition iterative code optimization整数分区迭代代码优化
【发布时间】:2016-03-24 04:37:50
【问题描述】:

我一直在编写代码来迭代地划分整数并使用以前的结果来完全划分数字,我的想法是使用以前的分区可以提高速度。到目前为止,我的性能比递归地对整数进行分区慢了 22 倍,并且由于快速耗尽内存而无法测试更大的数字。如果有人可以帮助优化代码,我将不胜感激。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

public class Summands {
  private static HashMap<Integer, HashSet<List<Integer>>> results;
  private static HashMap<Integer, HashSet<String>> recursiveResults;

  private static void sort(int[] a) {
    //Radix sort for int array
    int i, m = a[0], exp = 1, n = a.length;
    int[] b = new int[n];
    for (i = 1; i < n; i++) {
      if (a[i] > m) {
        m = a[i];
      }
    }
    while (m / exp > 0) {
      int[] bucket = new int[n];

      for (i = 0; i < n; i++)
        bucket[(a[i] / exp) % n]++;
      for (i = 1; i < n; i++)
        bucket[i] += bucket[i - 1];
      for (i = n - 1; i >= 0; i--)
        b[--bucket[(a[i] / exp) % n]] = a[i];
      for (i = 0; i < n; i++)
        a[i] = b[i];
      exp *= n;
    }
  }

  private static void generateResults(int n) {
    //iterative partitioning
    results.put(n, new HashSet<>());
    results.get(n).add(new ArrayList<>());
    for (List<Integer> list : results.get(n)) {
      list.add(n);
    }
    for (int i = 1; i <= Math.floorDiv(n, 2); i++) {
      //get all 2 summands partitions
      int a = n - i;
      results.get(n).add(Arrays.asList(i, a));
    }
    if (n > 1) {
      //Get the rest of the partitions
      HashSet<List<Integer>> set = new HashSet<>(results.get(n));
      for (List<Integer> equ : set) {
        if (equ.size() > 1) {
          if (equ.get(1) > 1) {
            HashSet<List<Integer>> temp = results.get(equ.get(1));
            for (List<Integer> k : temp) {
              List<Integer> tempEquList = new ArrayList<>(k);
              tempEquList.add(equ.get(0));
              int[] tempEqu = tempEquList.stream()
                      .mapToInt(Integer::intValue).toArray();
              sort(tempEqu);
              results.get(n).add(Arrays.stream(tempEqu)
                      .boxed().collect(Collectors.toList()));
            }
          }
        }
      }
    }
  }

  private static void recursivePartition(int n) {
    //recursively partition
    recursiveResults.put(n, new HashSet<>());
    partition(n, n, "", n);
  }

  private static void partition(int n, int max, String prefix, int key) {
    //recursive method for partitioning
    if (n == 0) {
      recursiveResults.get(key).add(prefix);
      return;
    }

    for (int i = Math.min(max, n); i >= 1; i--) {
      partition(n - i, i, prefix + " " + i, key);
    }
  }

  public static void main(String[] args) {
    //get number of partitions to get
    int target = Integer.valueOf(args[0]);
    //time the iterative version
    long time1 = System.currentTimeMillis();
    results = new HashMap<>(target);
    //loop until done
    for (int i = 1; i <= target; i++) {
      System.out.println(i);
      generateResults(i);
    }
    //time both methods
    long time2 = System.currentTimeMillis();
    recursiveResults = new HashMap<>(target);
    for (int i = 1; i <= target; i++) {
      //loop until done
      System.out.println(i);
      recursivePartition(i);
    }
    long time3 = System.currentTimeMillis();
    System.out.println("Iterative time: " + String.valueOf(time2 - time1));
    System.out.println("Recursive time: " + String.valueOf(time3 - time2));
    /*for (Integer key : results.keySet()) {
      //For ensuring proper amount of partitions
      //for lower numbers. Primarily for testing
      System.out.println(key + ": " + results.get(key).size());
    }*/
  }
}

【问题讨论】:

  • 我认为您必须提供一些上下文。 “整数分区”是什么意思?显示输入和输出示例。
  • 您没有使用维基百科文章中给出的明确公式有什么特别的原因吗?这似乎是一项简单的记忆工作,而不是您构建的极其复杂的东西。注意:由于您的代码似乎正在运行 - 如果速度很慢 - 您可能会更幸运地在 Code Review 上发布此内容。肯定会有人乐于按原样优化您的代码,但如果幸运的话,有些人可能会关注基础知识(数学/算法)。只是不要指望任何人在没有一点解释的情况下分析这个令人费解的混乱。
  • 如果您将 C++ 标签添加到您的问题中,我将为您发布优化的解决方案

标签: java optimization numbers number-theory integer-partition


【解决方案1】:

您可以使用mapToObjreduce 方法生成一组指定数字的和的组合,即整数分区。首先准备求和的数组集合,然后将这些集合的对依次相乘,得到笛卡尔积

Try it online!

int n = 7;
Set<int[]> partition = IntStream.range(0, n)
        // prepare sets of arrays of summands
        .mapToObj(i -> IntStream.rangeClosed(1, n - i)
            .mapToObj(j -> new int[]{j})
            // Stream<TreeSet<int[]>>
            .collect(Collectors.toCollection(
                // comparing the contents of two arrays
                () -> new TreeSet<>(Arrays::compare))))
        // intermediate output, sets of arrays of summands
        .peek(set -> System.out.println(
            set.stream().map(Arrays::toString).collect(Collectors.joining())))
        // sequential summation of pairs of sets up to the given number
        .reduce((set1, set2) -> set1.stream()
            // combinations of inner arrays
            .flatMap(arr1 -> {
                // sum of the elements of the first array
                int sum = Arrays.stream(arr1).sum();
                // if the specified number is reached
                if (sum == n) return Arrays.stream(new int[][]{arr1});
                // otherwise continue appending summands
                return set2.stream() // drop the combinations that are greater
                    .filter(arr2 -> Arrays.stream(arr2).sum() + sum <= n)
                    .map(arr2 -> Stream.of(arr1, arr2)
                        .flatMapToInt(Arrays::stream)
                        .sorted().toArray()); // the sorted array
            }) // set of arrays of combinations
            .collect(Collectors.toCollection( // two arrays that differ
                // only in order are considered the same partition
                () -> new TreeSet<>(Arrays::compare))))
        // otherwise an empty set of arrays
        .orElse(new TreeSet<>(Arrays::compare));
// final output, the integer partition of the specified number
partition.stream().map(Arrays::toString).forEach(System.out::println);

中间输出,加法数组集:

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

最终输出,指定个数的整数分区:

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

另见:Building permutation that sums to a number efficiently

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    相关资源
    最近更新 更多