【问题标题】:Algorithm to find all possible 'StringPairGroups' in Java?在 Java 中找到所有可能的“StringPairGroups”的算法?
【发布时间】:2011-11-26 18:29:24
【问题描述】:

数学/算法从来都不是我的强项(!)所以在这方面寻求帮助。

对于具有以下签名的方法,最有效的实现是什么:

/*
 * pairParts.size() > 0
 * pairParts.size() is always an even number
 */
private Set<StringPairGroup> getAllPossibleStringPairGroups(Set<String> pairParts) {
    Set<StringPairGroup> groups = new HashSet<StringPairGroup>();           
    // logic that adds all possible StringPairGroups            
`   return groups;
}

/*
 * StringPair object: first and second values cannot be null.
 * StringPair object: first != second
 * StringPair object: is equal to another if both the first values and both the second values are equal.
 */
public class StringPair {       
    private final String first;
    private final String second;    
    ...
}

/*
 * StringPairGroup object: is equal to another if their StringPair sets exactly match.
 */
public class StringPairGroup {      
   Set<StringPair> stringPairs  
   ...   
}

例如,输入 {'A', 'B'} 将返回 {[AB],[BA]}。 {'A', 'B', 'C', 'D'} 的输入将返回 {[AB],[BA],[AC],[CA],[AD],[DA][...],[AB,CD],[BA,CD],[AB,DC],[BA ,DC],[AC,DB],[...]}。

我真的只是对创建所有可能的 StringPairGroups 的逻辑感兴趣 对于任何一组输入字符串。我可能会想出某种蛮力实现,但我宁愿知道如何做一些更“聪明”的事情。

所以任何关于我将如何实现的提示都会很有用。

编辑:

对不起,我想我可能错过了一些非常重要的事情。我真的开始迷惑自己了。就是这样:

StringPairGroup 不能在其所有 StringPair 中包含重复的“pair part”。这有意义吗?

【问题讨论】:

  • 这些是字符串集合元素的组合——你可能需要阅读一些组合理论。
  • 组合理论? (!) - 谢谢,会调查它
  • 不,这不是家庭作业。我已经过了上学年龄(不幸的是!)
  • 我们可以假设 Set 中的顺序无关紧要吗?
  • 在比较 StringPairGroup 对象时,Set 中的顺序无关紧要

标签: java algorithm


【解决方案1】:

我会这样做:

  1. 在 StringPair 中实现比较,当传递另一个 StringPair 时,比较它们是否共享任何元素:boolean shareElement(StringPair other)
  2. 我可以创建所有可能的 StringPair 列表[AB]、[BA]、[AC]、[CA]
  3. 然后我会为 i = 1 - (originalList.size() / 2)...

    执行以下操作

    一个。从不共享任何元素的唯一对列表中创建 i 个元素的组合。

将此解决方案与@Hemal Pandya's 相结合,我想您会得到答案。意思是,将 Hemal 的集合递归组合与上面的 shareElement 结合使用。

编辑:我还会创建一个 boolean shareElement(StringPair... others)

【讨论】:

  • 通过创建所有可能的配对组合的列表来认为您正在做某事。午餐后将不得不再看一遍;)干杯
  • 请注意编辑 - 我想我错过了一些非常重要的事情
【解决方案2】:

我不知道这是否有效,但这是一个尝试..

在步骤 1 中给定A, B, C 生成[A,B], [A,C], [B, A], [B, C], [C, A], [C, B]

这很容易(对吧?)。

现在,最初您有一组 3 个元素,每个元素的大小为 1,您可以从中生成对。

在第一步之后,您已经设置了 6 个元素,每个元素的大小为 2,因此您可以执行与第一个元素相同的操作来生成二阶集合。

依此类推,直到你有一组 1 个元素。

有意义吗?

查看您的说明,订单无关紧要,我想这可能是不正确的。

【讨论】:

  • 这不会阻止集合中的重复条目,例如 [AB, AC]
  • 输入大小必须是偶数(所以A、B、C)不是有效输入。
  • 是的,但这并不意味着这个想法不合理。我认为前一个结果集中元素的递归组合是个好主意。它只需要检查以确保在单个组中没有重复使用元素。如果这个问题得到解决,它会得到我的 +1。
  • 谢谢@John。我需要回到我得到的报酬,但希望这会让 OP 开始 :)
【解决方案3】:

我会分两个阶段进行。首先,我将创建一个 Iterable 实现,该实现将具有一个将 Set 作为参数的构造函数。

然后我会将 Set 转换为数组(我使用 Set 而不是任何其他集合来保证输入集的唯一性)。如果你能保证没有并发访问,你真的不需要将它存储到数组中。

然后根据数组的大小,我计算可能的排列数并将其存储到最终变量中。我认为公式应该类似于 N!/2。

然后我创建了两个索引(或迭代器)。第一个 p 在第一个元素上,第二个 q 在第二个元素上。

每次调用 iterate 时,我会检查 q 是否在末尾,如果是,则将 q 重置为第一个元素并将 p 增加 1。如果 p 也在末尾,则表示没有更多元素。否则返回新的字符串对 p 和 q。

然后我可以使用这个迭代来创建一个新的字符串对集合、数组等。

分离可以节省内存,因为除非需要,否则不会存储字符串对组。

【讨论】:

    【解决方案4】:

    我理解的问题如下。有N个不同字符串的集合(N是偶数), 例如A B C D。 一对是来自此集合的两个不同字符串的任何串联。 所以,AB、BA 是对,但 AA 不是对。一对不同于另一对 如果相应的连接字符串是不同的字符串。所以,AB不同于BA, AB 等于 AB(显然)。可以构建 N*N - N 个不同的对 N 个不同的字符串。

    一组k阶是一组k个不同的对。因此,[AB]、[CD] 是 1 阶组 因为它们都包含 1 对。 [AB, CD], [BA, CD] 是 2 阶组,因为它们都包含 2 对。 两组相等当且仅当它们作为两个集合相等。所以,两个相等 组具有完全相同的对;对的顺序无关紧要。例如。, [AB, CD] 和 [BA, CD] 是不同的组,因为并非它们中的所有对都是相等的。 [AB, CD] 和 [CD, AB] 是两个相等的组。

    所有k阶组都可以递归构造:

    1. 选择任意一对 P 字符串
    2. 如果 k = 1,则返回由所有这些对组成的组
    3. 如果 k > 1:
      3.1 从 N 个字符串的集合 C(N) 中删除这对,留下一个集合 C(N-2) 剩余的字符串。
      3.2 从 C(N-2) 构造所有 k-1 阶组,并与 P 对组合。

    这是一个Java程序(完整代码在github:gist)。 ideone 上的一个可执行程序。

    public static class Pair {
        public String s1, s2;
        public Pair(String s1, String s2) {
            this.s1 = s1; this.s2 = s2;
        }
        public String toString() {
            return s1 + s2;
        }
    }
    
    public static class Group {
        public List<Pair> pairs = new ArrayList<Pair>();
        public Group(Pair p) {pairs.add(p);}
    }
    
    public static List<Group> getGroups(String[] strings, int order) {
        List<Group> groups = new ArrayList<Group>();
        for (int i = 0; i < strings.length; ++i) {
            for (int j = 0; j < strings.length; ++j) {
                if (i != j) {
                    Pair p = new Pair(strings[i], strings[j]);
                    if (order == 1) {
                        groups.add(new Group(p));
                    }
                    else {
                        String[] strings2 = new String[strings.length - 2];
                        for (int k = 0, k2 = 0; k < strings.length; ++k) {
                            if (k != i && k != j) {
                                strings2[k2++] = strings[k];
                            }
                        }
                        List<Group> groups2 = getGroups(strings2, order - 1);
                        for (int k = 0; k < groups2.size(); ++k) {
                            Group g = new Group(p);
                            groups.add(g);
                            Group g2 = groups2.get(k);
                            g.pairs.addAll(g2.pairs);
                        }
                    }
                }
            }            
        }
        return groups;
    }
    

    有 N/2 个可能的订单。为所有这些订单构建组并附加它们。

    String strings[] = {"A", "B", "C", "D", "E", "F"};
    List<Group> groups = new ArrayList<Group>();
    for (int order = 1; order <= strings.length/2; ++order) {
        List<Group> groups2 = getGroups(strings, order); 
        groups.addAll(groups2);
    }           
    

    递归解决方案很好理解,但效率较低。如果你的 N 很大 那么您将需要一个更快的迭代解决方案。迭代解少 比递归的更具有说明性,不适合在此演示。 您可以咨询例如Knuth:计算机编程的艺术,卷。 4A:组合算法。

    【讨论】:

    • 哇!这看起来很棒。明天或周末会好好看看。之后会带着一些cmets回来。谢谢。
    • 再次感谢。现在理解它,虽然已经意识到当 N 很大时数字有多大,因此将研究更快的迭代解决方案。您对如何解决这个问题有任何提示吗?
    • 这是一个很大的话题。你可以在网上搜索“所有组合算法”(和类似的)。你会发现例如这个overwiew。您也可以查看我提到的 Knuth 的书 - 但它相当困难,您需要将他的算法转换为 Java。无论如何,我认为您需要根据您的具体问题调整任何找到的算法。希望我的递归解决方案为您提供了想法和基准。
    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2018-09-25
    • 2019-11-27
    • 2021-10-04
    • 2017-08-27
    • 2016-08-04
    • 1970-01-01
    相关资源
    最近更新 更多