【问题标题】:Loop i and j, where i != j循环 i 和 j,其中 i != j
【发布时间】:2014-03-01 01:46:55
【问题描述】:

例如,我需要获取具有数字 {0, 1, 2} 的 2 值数组的所有组合,其中这两个数字不相同。 我明白了

0 1
0 2
1 0
1 2
2 0 
2 1

我忽略了

0 0
1 1
2 2

现在我用

  for (int i= 0; i < L ; i++) {
                for (int j = 0; j < L; j++) {
                    if (i!= j) {

但它很慢? 有什么解决办法吗? L 将 > 4000。

我正在做的是找到将矩阵拆分为 4 个子矩阵的每个组合

例子:

 3 | 0   2  -8  -8
 5 | 3   2   2   3
 2 | 5   2   1   4
 -------------------
 3   4  -1 |  4  2
-3   6   2 |  4  3

并使用 sum-table 计算它们的总和。

相关问题:Split matrix into 4 sub-matrices with lowest difference between their sum

所以对于 matix,我有一条水平线和两条垂直线,我正在计算 4 个矩阵的总和,但两条垂直线不应该产生一条大垂直线,所以 i != j。

UPDATE 1 对的顺序是相关的

【问题讨论】:

  • 您在谈论至少 15,996,000 种组合。是的,它会很慢。
  • 其实它是在另一个for循环中循环,所以我真的需要一个手
  • @JohnDow:你需要检查所有 1600 万个组合吗?因为没有一种更快的方式来做你想做的事。
  • @JohnDow 你无法回避这样一个事实,即有 1600 万种组合。那只是数学。你真正想要的是只生成你真正需要的东西——如果没有更多关于你使用这些数字的信息,我们无法帮助你。
  • 这里只是挑剔,但由于每个 2 值数组中的数字顺序是相关的,因此您要查找的词是 permutations,而不是 组合.

标签: java math combinations combinatorics


【解决方案1】:

您可以通过以下方式提高性能:

for (int i= 0; i < L ; i++) {
                for (int j = i + 1 ; j < L; j++) {
System.out.println.(i + " " + j + "\n" + j + " " + i);
}}

【讨论】:

  • @SSpoke 为什么?复杂性可能相同,但运行速度会更快(~一半时间)
  • @goten - 不正确。它仍然是 O(N^2)。实际上,循环会执行内部循环体L*(L-1)/2 次。
  • 两种情况的复杂度都是 O(N²)。在 goten 的优化版本中,他将通过外部循环运行 N 次,平均 (N/2) 次通过内部循环。
  • @MirroredFate 这么说来,既然打印语句的数量是一样的,那答案真的提升不到0.02%吗?
  • 使用 JohnDow 的原始方法,带有if 块和其中的两个打印语句,当L=4000 时,我需要210962 milliseconds 才能运行。使用goten的方法,我花了100591 milliseconds
【解决方案2】:

如果对的顺序是相关的(例如,您希望将 {1, 2}{2, 1} 视为不同),则可以在此级别上获得显着的加速。

如果对的顺序无关紧要(例如,您考虑过{1, 2},您可以跳过{2, 1}),那么@goten 的答案会给您2 倍的加速。 更新问题已更新以确认订单相关...因此@goten 的解决方案不适用。

您不太可能通过微优化获得显着的加速;例如将其转换为 while 循环等。无论如何,JIT 编译器很可能在幕后进行等效优化。

如果你想要更好的加速,你需要改变你的整体算法,以便你不需要考虑这么多的组合。这是否可行将取决于您的问题。

要考虑的另一种选择是并行处理组合。但是,这仅在您有多个内核时才有用……并且您的问题/算法适合并行化。

更新 - 您的问题(根据更新后的问题),看起来它应该适合并行化,但您可能需要进行一些仔细的调整以尽量减少影响访问大型共享输入数组时的内存争用。

【讨论】:

    【解决方案3】:

    对于给定的极点,c是极点的大小

     long[] radky = new long[x];
            long cisloRadku = 0;
    
    
        long min = 0;
        int q;
        int radek = 0;
    
        for (q = 0; q < (x); q++) {
            for (int j = 0; j < x; j++) {
                radky[q] += pole[q][j];
    
            }
    
            cisloRadku += radky[q];
        }
    
    
        long hodnota1 = 0;
        long hodnota2 = 0;
    
        for (int i = 0; i < (x - 1); i++) {
            hodnota1 += radky[i];
            hodnota2 = cisloRadku - hodnota1;
    
    
            long druheMin = hodnota1 - hodnota2;
    
            if (druheMin < 0) {
                druheMin = druheMin * (-1);
            }
            if (i == 0) {
                min = druheMin;
            }
            if (min > druheMin) {
                min = druheMin;
                radek = i;
            }
        }
    
        long[][] poleHorni = new long[radek + 1][x];
        long[][] poleDolni = new long[x - (radek + 1)][x];
    
    
        for (int i = 0; i < (radek + 1); i++) {
            for (int j = 0; j < x; j++) {
                poleHorni[i][j] = pole[i][j];
    
            }
    
        }
    
    
        long[] soucetHornich = new long[x];
        long soucetSlopucuUp = 0;
        for (int k = 0; k < x; k++) {
            for (int j = 0; j < radek + 1; j++) {
                soucetHornich[k] += poleHorni[j][k];
            }
    
            soucetSlopucuUp += soucetHornich[k];
        }
    
    
        long cislo1 = 0;
        long cislo2;
        int sloupec = 0;
        long min2 = 0;
        for (int i = 0; i < (x - 1); i++) {
            cislo1 += soucetHornich[i];
            cislo2 = soucetSlopucuUp - cislo1;
    
            long druheMin2 = cislo1 - cislo2;
    
    
            if (druheMin2 < 0) {
                druheMin2 = druheMin2 * (-1);
            }
            if (i == 0) {
                min2 = druheMin2;
                vysledky[0] = cislo1;
                vysledky[1] = cislo2;
            }
            if (min2 > druheMin2) {
                min2 = druheMin2;
                sloupec = i;
                vysledky[0] = cislo1;
                vysledky[1] = cislo2;
            }
        }
    
    
        int a = 0;
        int b = 0;
    
        for (int i = (radek + 1); i < (x); i++) {
            for (int j = 0; j < x; j++) {
                poleDolni[a][b] = pole[i][j];
    
                b++;
            }
            b = 0;
            a++;
    
        }
    
    
        long[] soucetDolnich = new long[x];
        long soucetSlopucuDown = 0;
    
        for (int k = 0; k < x; k++) {
            for (int j = 0; j < (x - (radek + 1)); j++) {
                soucetDolnich[k] += poleDolni[j][k];
            }
    
            soucetSlopucuDown += soucetDolnich[k];
        }
    
        long cislo1A = 0;
        long cislo2A;
        long min2A = 0;
        for (int i = 0; i < (x - 1); i++) {
            cislo1A += soucetDolnich[i];
            cislo2A = soucetSlopucuDown - cislo1A;
    
            long druheMin2A = cislo1A - cislo2A;
    
    
            if (druheMin2A < 0) {
                druheMin2A = druheMin2A * (-1);
            }
            if (i == 0) {
                min2A = druheMin2A;
                vysledky[2] = cislo1A;
                vysledky[3] = cislo2A;
            }
            if (min2A > druheMin2A) {
                min2A = druheMin2A;
    
                vysledky[2] = cislo1A;
                vysledky[3] = cislo2A;
            }
    
    
    
        long[] vyslRozdil = new long[6];
        vyslRozdil[0] = vysledky[0] - vysledky[1];
        vyslRozdil[1] = vysledky[0] - vysledky[2];
        vyslRozdil[2] = vysledky[0] - vysledky[3];
        vyslRozdil[3] = vysledky[1] - vysledky[2];
        vyslRozdil[4] = vysledky[1] - vysledky[3];
        vyslRozdil[5] = vysledky[2] - vysledky[3];
        long max = 0;
        long max2;
    
        for (int i = 0; i < 6; i++) {
            if (vyslRozdil[i] < 0) {
                vyslRozdil[i] *= -1;
            }
            max2 = vyslRozdil[i];
            if (i == 0) {
                max = max2;
            }
            if (max < max2) {
                max = max2;
            }
        }
    
        System.out.println(max);
    

    【讨论】:

      【解决方案4】:

      可能能够从中获得一些好处

      for (int i= 0; i < L ; ++i) {
        int j=0;
        for (; j < i; ++j) {
        }
        for (++j; j < L; ++j) {
        }
      }
      

      这可以避免在您仍在测试 i 时测试 L。我也切换到了预增量——因为你没有使用结果它可能在优化器完成它之后没有任何区别,但是从概念上讲,后增量是更多复杂的操作,除非碰巧有指令集支持。

      如果您可以更改报告结果的顺序,这会更快:

      int L1=L-1;
      for (int i=L1; i >=0 ; --i) {
        int j=L1;
        for (; j > i; --j) {
        }
        for (--j; j >=0; --j) {
        }
      }
      

      针对大多数值进行测试需要减法。针对零的测试不会;它在加载值时“免费”发生。事实上,你可以进一步改进它:

      int L1=L-1, i=L1;
      do {
        int j=L1;
        do {
        } while(--j>i);
        while(--j>=0)
        {
        }
      } while(--i >= 0);
      

      这避免了多余的初始测试。 (请注意,您仍然需要在第二个循环的顶部进行测试,以防 i 为 0。)

      但是,实际上循环开销是您的问题中最少的; System.out.println() 调用将消耗比循环控制更多的周期。

      【讨论】:

        猜你喜欢
        • 2014-11-25
        • 1970-01-01
        • 2016-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-19
        • 1970-01-01
        • 2012-10-20
        相关资源
        最近更新 更多