【问题标题】:Faster way to generate permutation [closed]生成排列的更快方法[关闭]
【发布时间】:2013-12-05 02:13:18
【问题描述】:

我正在开发一个计算两个字符串(A 和 B)之间的命中百分比的程序。为了获得准确的百分比,我将 N-Grams 与作为字符串 A 排列的字符串列表进行匹配。

这是我的代码

public String[] generatePermutations( String name ){

    String[] perms = new String[ calcN(name.length()) ];
    int nameLen = name.length(),
                cnt = 0;


    for(int i = 0; i < name.length(); i++ ){

        nameLen = name.length()-i;
        for( int ii = 0; ii <= i; ii++){
            perms[cnt++] = name.substring( ii, ii + nameLen );
        }
    }
    return perms;
}

参考calcN()在下面

public int calcN( int n ){
    return ( n * (n+1 )) / 2;
}

给定一个字符串“ABC”,此方法将生成

{“A”、“B”、“C”、“AB”、“BC”、“ABC”}

由于我执行此操作数千次(可能是数十万次),有什么办法可以从 CPU 中挤出几个额外的周期? (除了切换到 C++ 或 C)。一如既往,提前感谢您的建议!

【问题讨论】:

  • 这个问题似乎是题外话,因为它是关于代码改进而不是代码修复。此类问题请访问CodeReview
  • 需要指出的是substring在不同jvm中的实现方式不同。有些使用作为原始字符串基础的单个不变数组,有些则将数组复制到新字符串中。
  • 有没有办法找出哪个是哪个,或者强制 JVM 使用一个?我在 3.5ghz i7-4770K 上运行 Win7,64 位
  • @Kyte java -version 会告诉你正在运行哪一个(版本和构建)。如果它没有说 OpenJDK,那它就不是 OpenJDK。您可以从openjdk.java.net 下载 OpenJDK JVM

标签: java string optimization permutation n-gram


【解决方案1】:

该方法的性能优化部分取决于所使用的 JVM。例如,在 OpenJDK 中子字符串实现为:

public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > count) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    if (beginIndex > endIndex) {
        throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
    }
    return ((beginIndex == 0) && (endIndex == count)) ? this :
        new String(offset + beginIndex, endIndex - beginIndex, value);
}

该字符串构造函数是一种受保护的形式,实现为:

 // Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
     this.value = value;
     this.offset = offset;
     this.count = count;
 }

请注意,这不需要创建新值(支持字符串的 char[])。

另一方面,正如Java 7 Performance Tuning Guide 中所述,由于内存泄漏(被垃圾回收的 1000 个字符长字符串的单个字符子字符串仍保留支持它的 1000 个字符的字符串),它已被删除。

因此,选择使用哪个 jvm 可能会对从子字符串创建字符串产生重大影响。

根据您的应用程序以及性能的重要性,您可能会考虑实现自己的有限版本的 String,它重新实现了子字符串的偏移量和长度实现。

【讨论】:

  • 感谢您的详细回复。除非我决定大幅扩大数据集的大小,否则性能应该不是问题,因此排列生成器的效率并不是关键。谢谢!
  • 您可以根据Changing JVM in Java 中的描述为您所在的适当环境调整您正在使用的JVM。这可能是一个有趣的测试(为了有趣),看看不同 JVM 上的性能如何,并且有足够大的数据集来显示事情在哪里变得不同。
【解决方案2】:

我知道,我可能不会帮忙,但我无法抗拒。

Scala 单线:

(1 to 3).flatMap("ABC".combinations(_))

返回

Vector(A, B, C, AB, AC, BC, ABC)

"ABC".permutations.toList

返回

List(ABC, ACB, BAC, BCA, CAB, CBA)

在 JVM 之上使用 Scala 非常容易。

【讨论】:

  • 请注意,“AC”和“BAC”不是结果集的一部分。似乎所需的集合被描述为“仅向前,不跳过”。本质上,索引为:1、2、3、1-2、2-3、1-3的字符串。
  • 是的,他说的是排列,但举了“仅子字符串”之类的例子。我不是在解决我只是想展示 Scala 语言的强大功能的问题。
【解决方案3】:

没有多大帮助,但我看不到cnt的使用,它只是使用ii

【讨论】:

  • 编辑代码以删除变量。这是该方法的早期迭代遗留下来的。
  • 好吧,我认为您的代码现在是错误的 - 相同的 perms[cnt] 将被更新多次。
  • 已编辑的代码解释了使用“cnt”变量的目的。
  • 看起来和原来一样。 cnt 何时会与 ii 不同?
  • 当 ii = 1 且 i 仍被分配 0 值。请注意,有三个单字符条目、两个双字符条目和一个三字符条目。
猜你喜欢
  • 1970-01-01
  • 2022-10-04
  • 2020-06-15
  • 2013-12-23
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
相关资源
最近更新 更多