【问题标题】:Is there a way to calculate the indices generated by nested for loops?有没有办法计算嵌套 for 循环生成的索引?
【发布时间】:2017-05-27 01:19:25
【问题描述】:

我目前正在考虑如何存储经常访问的数据。它的目的是存储在一个数组中,目前是通过以下方式生成的。

public static void generateData() {

    int index = 0;
    for(int a1 = 0; a1 < 52; a1++) {
        for(int a2 = a1 + 1; a2 < 52; a2++) {
            for(int a3 = a2 + 1; a3 < 52; a3++) {
                for(int a4 = a3 + 1; a4 < 52; a4++) {
                    for(int a5 = a4 + 1; a5 < 52; a5++) {
                        for(int a6 = a5 + 1; a6 < 52; a6++) {
                            for(int a7 = a6 + 1; a7 < 52; a7++) {
                                data[index++] = compute(a1,a2,a3,a4,a5,a6,a7);
                            }
                        }
                    }
                }
            }
        }
    }
}

我现在的问题是使用 a1 到 a7 参数快速访问计算数据。我能想到的唯一方法是在迭代期间进行类似的迭代,直到参数相同,就像这样

public static int getIndex(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
    int index = 0;
    for(int a1 = 0; a1 < 52; a1++) {
        for(int a2 = a1 + 1; a2 < 52; a2++) {
            for(int a3 = a2 + 1; a3 < 52; a3++) {
                for(int a4 = a3 + 1; a4 < 52; a4++) {
                    for(int a5 = a4 + 1; a5 < 52; a5++) {
                        for(int a6 = a5 + 1; a6 < 52; a6++) {
                            for(int a7 = a6 + 1; a7 < 52; a7++) {

                                if(a1 == i1 && a2 == i2 && a3 == i3 && a4 == i4 && a5 == i5 && a6 == i6 && a7 == i7) {
                                    return index;
                                } else {
                                    index++;
                                }

                            }
                        }
                    }
                }
            }
        }
    }

    throw new IllegalArgumentException();
}

但是,这种方法仅在线性时间运行,考虑到数据量,速度不够快。因此,我想知道是否有任何方法可以以常数或对数时间计算指数。 因为我的问题可能看起来很模糊。我正在存储每个可能的Texas hold'em 手牌的所有可能结果,以进行进一步的测试。

【问题讨论】:

  • 这更像是一道数学题而不是编程题
  • 由于数据已排序,因此您可能希望对每个 a1、a2、...、a7 值使用二进制搜索,然后计算索引。例如:索引 = a1 的建立索引 * a2 的建立索引 * a3 的建立索引 ...

标签: java arrays performance for-loop


【解决方案1】:

您可以考虑将参数作为字符串键存储在映射到所需索引的映射中。所以你可以在课堂上的某个地方声明Map&lt;String,Integer&gt; indexMap = new HashMap&lt;&gt;();

然后在您的 generateData 方法中,您可以将参数存储为一个大字符串键。

    indexMap.put(Integer.toString(a1)+Integer.toString(a2)+ ... +
    Integer.toString(a7),index);

只需确保在递增索引之前存储索引即可。这将允许您这样做

    return indexMap.get(Integer.toString(a1)+Integer.toString(a2)+ ... +
    Integer.toString(a7));

您应该对未找到键的错误进行错误检查,但这将使您能够更快地检索到所需的索引。

【讨论】:

    【解决方案2】:

    我会使用HashMap 来保留a1..a7 -&gt; compute value,因为我不确定是否有有效的算法可以在合理的时间内从a1 ... a7 检索索引。但我会使用 Long 作为 Map 键并以下一种方式构造它

    long key = (((((a1 * 100L + a2) * 100 + a3) * 100 + a4) * 100 + a5) * 100 + a6) * 100 + a7
    

    或作为函数

    public static long getKey(int ... array) {
        if (array.length != 7) {
            throw new IllegalArgumentException();
        }
        long value = 0;
        for (int item : array) {
            value = value * 100 + item;
        }
        return value;
    }
    

    这将使搜索、密钥生成和空间消耗变得高效(地图将包含 133784560 个条目)。

    注意a1 乘以100L,否则结果会不正确,因为它不适合 Integer 范围。

    【讨论】:

    • 这是一个糟糕的解决方案,因为它需要千兆字节的堆内存才能从 7 个整数计算索引。
    【解决方案3】:

    在您的情况下,index 可以被视为Combinatorial number system 中的一个数字,其中循环变量a1...a7 是这个数字的数字。

    这是一个根据给定循环变量计算索引的函数。

    public static int getIndex(int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
        return 133784559 - (C7[a1] + C6[a2] + C5[a3] + C4[a4] + C3[a5] + C2[a6] + (51 - a7));
    }
    
    static final int[] C2 = Ck(2), C3 = Ck(3), C4 = Ck(4), C5 = Ck(5), C6 = Ck(6), C7 = Ck(7);
    
    // Creates a cache of C(51-i, k) for 0 <= i < 52
    static int[] Ck(int k) {
        int[] result = new int[52];
        for (int i = 0; i < 52; i++) {
            result[i] = (int) C(51 - i, k);
        }
        return result;
    }
    
    // Computes binomial coefficient C(n, k)
    static long C(int n, int k) {
        long C = 1;
        for (int i = 0; i < k; i++) {
            C = C * (n - i) / (i + 1);
        }
        return C;
    }
    

    getIndex 方法非常快,只需要大约 1K 的额外静态内存。

    【讨论】:

    • 完全符合我的要求。 +1
    猜你喜欢
    • 1970-01-01
    • 2020-09-14
    • 2023-03-27
    • 1970-01-01
    • 2020-07-07
    • 2019-01-27
    • 2020-01-06
    • 2022-11-18
    • 1970-01-01
    相关资源
    最近更新 更多