关于文本细分的投资组合优化

时间:2018-02-13 10:19:59

标签: algorithm line-breaks portfolio np-complete

给出一系列单词和行号n。
如何获得最小宽度,小于n行。
例如,
字词: [“AA”,“AA”,“A”,“AAAAAA”,“AA”] 行: 2
我们可以得到 [
   “AA AA A”,
   “AAAAAA AA”,//这条线是最宽的,所以我们将其视为宽度
]
所以答案是8 如果我们将行更改为3,那么我们就可以得到 [
   “AA AA A”,
   “AAAAAA”,//这条线是最宽的,所以我们将其视为宽度
   “AA”
]
所以答案是6.
通知
字词: [“AA”,“A”,“AA”,“A”,“AA”] 行: 5
我们只能获得3行结果,答案是3
[
   “AA A”,“AA A”,“AA” ]
因为如果我们减小宽度,结果将变为
[
   “AA”,
   “A”,
   “AA”,“A”,“AA” ]
我想知道此问题是否为NP-Complete以及是否存在任何类似问题。

3 个答案:

答案 0 :(得分:0)

是的,这个问题是NP-Complete。您的问题是Bin Packing Problem

的变体

您的案例的贪婪算法可能如下所示:按长度(降序)对单词进行排序,并按此顺序处理它们。将每个字符串放在具有最小宽度的行中(如果有多个具有相同宽度的行,则只选择一个)。

答案 1 :(得分:0)

我对NP-Completeness不太确定。

假设我们需要一个空格来分隔单词。这与您在示例中显示的内容相符,但不符合您的计算方式:" AA A"我认为宽度为4。

我们有一个长度为单词的列表(2,2,1,6,2),并希望将它分成n行。

所以总长度是长度(13)+元素数量-1的总和,因为每对的一个分隔符, - 构建自然分隔符的行数,+ 1 => list.lenghts.sum + list.count - 行。

因此对于2行,这是18 - 2 = 16,对于3行18 - 3 = 15。

我们将这个长度除以线,以获得每条线的最佳长度,即2条线16/2 = 8,3条线15/3 = 3.如果从分区休息,我们有围捕。

现在我们用输入填充递归函数。对于当前行,只要行低于最佳值,我们就会收集。如果再添加一个单词会超过最佳单词,我们会比较,添加单词是否会超过该行,而不是省略单词会浪费空间。我们寻求较小的差异并重复其余的程序。

但是我还没有对这个恶意制作的输入进行测试。

class Linesplitter (text: String) {
    val words = text.split (" ")
    val count = words.length
    val sizes = words.map(_.length)
    val total = sizes.sum + count // including delimiters and + 1

    def delta (a: Int, b:Int) = if (a < b) b-a else (a-b)

    def split (lines: Int): List[List[String]] = {
    //
        def betterToAdd (currentLen: Int, lenToAdd: Int, opt: Int) : Boolean = {
            val dsmall = delta (currentLen, opt)
            val dlong  = delta (opt, currentLen + 1 + lenToAdd)
            dsmall > dlong
        }

        def optimalSplit (lv: List[String], currentLine: List[String], currentLen: Int, opt: Int): List[List[String]] = lv match {
            case Nil => List (currentLine.reverse)
            case x :: xs => if (betterToAdd (currentLen, x.length, opt)) {
                    optimalSplit (xs, x :: currentLine, currentLen + 1 + x.length, opt)
                } else {
                    currentLine.reverse :: optimalSplit (xs, List (x), x.length, opt)
                }
        }
        /* just printing, not generating any return
        def printOptimalSplit (lv: List[String], currentLine: List[String], currentLen: Int, opt: Int): Unit = lv match {
            case Nil => println (currentLine.reverse.mkString (" "))
            case x :: xs => if (betterToAdd (currentLen, x.length, opt)) {
                    printOptimalSplit (xs, x :: currentLine, currentLen + 1 + x.length, opt)
                } else {
                    println (currentLine.reverse.mkString (" "))
                    printOptimalSplit (xs, List (x), x.length, opt)
                }
        }
        */

        val optimum = (total - 1) / lines
        // println (f"n: $lines%2s   Optimum: $optimum")
        optimalSplit (words.toList, List(), 0, optimum)
    }
}

val ls = new Linesplitter ("""Seit je hat Aufklärung im umfassendsten Sinn fortschreitenden Denkens das Ziel verfolgt, von den Menschen die Furcht zu nehmen und sie als Herren einzusetzen. Aber die vollends aufgeklärte Erde strahlt im Zeichen triumphalen Unheils. Das Programm der Aufklärung war die Entzauberung der Welt. Sie wollte die Mythen auflösen und Einbildung durch Wissen stürzen. Bacon, »der Vater der experimentellen Philosophie«[1], hat die Motive schon versammelt. Er verachtet die Adepten der Tradition, die »zuerst glauben, daß andere wissen, was sie nicht wissen; und nachher, daß sie selbst wissen, was sie nicht wissen. Leichtgläubigkeit jedoch, Widerwille gegen den Zweifel, Unbesonnenheit im Antworten, Prahlerei mit Bildung, Scheu zu widersprechen, Interessiertheit, Lässigkeit in eigener Forschung, Wortfetischismus, Stehenbleiben bei bloßen Teilerkenntnissen: dies und Ähnliches hat die glückliche Ehe des menschlichen Verstandes mit der Natur der Dinge verhindert, und ihn statt dessen an eitle Begriffe und planlose Experimente verkuppelt: die Frucht und Nachkommenschaft einer so rühmlichen Verbindung kann man sich leicht vorstellen. Die Druckerpresse, eine grobe Erfindung; die Kanone, eine die schon nahe lag; der Kompaß, in gewissem Grad bereits früher bekannt: welche Veränderung haben nicht diese drei hervorgebracht - die eine im Zustand der Wissenschaft, die andere in dem des Krieges, die dritte in dem der Finanzen, des Handels und der Schiffahrt! Und auf diese, sage ich, ist man nur zufällig gestolpert und gestoßen. Also die Überlegenheit des Menschen liegt im Wissen, das duldet keinen Zweifel. Darin sind viele Dinge aufbewahrt, welche Könige mit all ihren Schätzen nicht kaufen können, über die ihr Befehl nicht gebietet, von denen ihre Kundschafter und Zuträger keine Nachricht bringen, zu deren Ursprungsländern ihre Seefahrer und Entdecker nicht segeln können. Heute beherrschen wir die Natur in unserer bloßen Meinung und sind ihrem Zwange unterworfen; ließen wir uns jedoch von ihr in der Erfindung leiten, so würden wir ihr in der Praxis gebieten.«""")
(20 to 100 by 20).map (i => List ("\n") :: ls.split (i)).foreach {_.foreach (l => println (l.mkString (" ")))}

为lines = 60生成的示例输出:

Seit je hat Aufklärung im umfassendsten
Sinn fortschreitenden Denkens das
Ziel verfolgt, von den Menschen die
Furcht zu nehmen und sie als Herren
einzusetzen. Aber die vollends
aufgeklärte Erde strahlt im Zeichen
triumphalen Unheils. Das Programm
der Aufklärung war die Entzauberung
der Welt. Sie wollte die Mythen
(...)

答案 2 :(得分:0)

如果将数组拆分为连续的部分,则此问题不是NP-Complete。在这种情况下,我们可以在目标宽度上使用二进制搜索来解决它。 O(w log n)时间,n是字符总数,w数组中的字总数。