【问题标题】:How can I find a permutation of all the digits of a given number such that it is closest to the target number如何找到给定数字的所有数字的排列,使其最接近目标数字
【发布时间】:2018-04-03 15:05:42
【问题描述】:

我刚从一本书中看到这个有趣的问题,但我找不到答案。

我有一个给定的数字 X 和一个目标数字 Y,任务是找到 X 的所有数字的排列,使其最接近 Y。 数字是数组的形式。那里没有给出数组大小限制。

例子

Given number X = 1212
Target number Y = 1500
Answer = 1221
Here, abs(1500-1221) is smallest among all permutations of X.

Given number X = 1212
Target number Y = 1900
Answer = 2112
Here, abs(1900-2112) is smallest among all permutations of X.

Given number X = 1029
Target number Y = 2000
Answer = 2019
Here, abs(2000-2019) is smallest among all permutations of X.

我能找到的解决方案之一是生成给定数字的所有排列,并在每个阶段计算差值。但这很慢。

我试图找到贪婪的方法,我将遍历目标数字 Y 的所有索引,并在每个索引处放置给定数字 X 的那个数字,使得 abs(Y[i] - X[i ]) 是最小值。但这在很多情况下都失败了。

我正在尝试一种 DP 方法,但无法提出任何方法。

任何导致答案的线索都会有所帮助。

编辑 - 为我的贪婪方法添加伪代码

for each index i in [0,Y]:

    min_index = 0;
    for each index j in [1, X.length]:
        if abs(X[j] - Y[i]) < abs(X[min_index] - Y[i]):
            min_val = j

    print X[min_index]
    remove min_index from X

Example X = 1212 and Y = 1900.
step 1 - output 1 and remove index 0 from X.
step 2 - output 2 and remove index 1 from X.
step 3 - output 1 and remove index 2 from X.
step 2 - output 1 and remove index 3 from X.
answer = 1212 which is wrong (correct answer is 2112).
So fails for this test case and lots more.

【问题讨论】:

  • 你能提供你贪婪方法的伪代码吗?在这种情况下它会失败吗?因为你的贪婪对我来说似乎是正确的。
  • @PhamTrung 我已经编辑了这个问题。看看吧。
  • 这个问题有什么限制吗?
  • 没有。这也是我想知道的。我正在尝试获得任何多项式时间解决方案。

标签: algorithm permutation


【解决方案1】:

所以,问题可以看成如下:

从最大有效数字开始,对于这些索引中的每一个,有三种情况:

  • 当前数字将小于所需数字,因此对于其余数字,我们尝试创建可能的最大数字 => 对于其余数字,我们按降序对它们进行排序,即如果我们还剩下 0、2、7、5 -> 我们将创建 7520

  • 当前数字将大于所需数字,因此对于其余数字,我们尝试创建可能的最小数字 => 对于其余数字,我们按升序对它们进行排序,即如果我们还剩下 0、2、7、5 -> 我们将创建 0275

  • 如果当前数字等于所需数字,我们会将其附加到prefix 并尝试在下一次迭代中找到更好的匹配。

伪代码:

int prefix, result;
for each index i from 0 to Y.length() {

       int larger = prefix + smallestDigitLargerThan(Y(i)) + OtherDigitInAscendingOrder;
       int smaller = prefix + largestDigitSmallerThan(Y(i)) + OtherDigitInDescendingOrder;
       update result based on larger and smaller;
       if there is no digit equals to Y(i)
          break;
       else {
          remove Y(i) in X
          prefix = prefix*10 + Y(i)
       } 
    }
}
if prefix == Y {
   //We have a full match
   return prefix;
}
return result;

例如

X = 1029
Y = 2000

At index 0 -> Y(0) = 2, 

int smaller = 0 (prefix) + 1(largest digit that is less than 2) + 920 (other digit in descending order) = 1920
int larger = 0 (prefix) + 9(smallest digit that is greater than 2) + 012 (other digit in ascending order) = 9012
int result = 1920
int prefix = 2

At index 1 -> Y(1) = 0,

int smaller = //Not exist
int larger = 2 + 1 + 09 = 2109
int result = 1920
int prefix = 20

At index 2 -> Y(2) = 0,

int smaller = //Not exist
int larger = 20 + 1 + 9 = 2019
int result = 2019
//Break as there is no digit match Y(2) = 0 from X

其他例子:

X = 1212
Y = 1500

At index 0 -> Y(0) = 1, 

int smaller = //Not exist
int larger = 0 + 2 + 112 = 2112
int result = 2112
int prefix = 1

At index 1 -> Y(1) = 5,

int smaller = 1 + 2 + 21 = 1221
int larger = //Not exist
int result = 1221
//Break from here as there is no digit match Y(1) = 5 in X

【讨论】:

  • 算法中的“期望数字”是什么?你能举个例子(最好是最后一个例子)并展示模拟。谢谢
  • @AbhishekKumar 添加了示例。请检查伪代码和示例,从中应该很清楚。
  • 如果我们没有在 X 中得到数字匹配 Y(i),这意味着现在我们不关心剩余的数字。就像在您的上一个示例中 Y = 1500 一样,我们仅根据前两位数计算我们的解决方案。 15XX 的答案是相同的,其中 X 是任意数字。我说的对吗?
  • @AbhishekKumar 是的,你是对的,如果没有匹配,只有两种情况,更小或更大,可以通过上面的贪心解决,然后,我们可以从那里打破.
  • @AbhishekKumar 其实我觉得有更好的办法解决,假设X和Y都有第一个n数字匹配,例如我们有X = 123XXX和Y = 123YYY 所以,这里n = 3,所以,我们可以安全地匹配第一个n-1 数字,并且只使用我的算法从数字n 开始,最多需要两步。
【解决方案2】:

Beam search 宽度为 3 可能是一种方法。这个想法是从最大到最小的数字构造数字,其余的用零填充。您在每一步为梁中的每个数字构建最接近和次接近的数字,并丢弃所有比前三个差的数字。 (事实上​​,您最多需要两个光束大小。如果光束中两个条目的距离相等,则只需要三个的情况。)在计算期间,构造的数字AB不应该永远相等(X 只包含相同数字的特殊情况除外。)

这是第二个示例的梁。 * 表示最佳光束,没有 * 表示两者都一样好:

2000* -> 2100* -> 2112*
         2200  -> 2211
1000  -> 1200  
         1100

这是第一个例子:

1000  -> 1200* -> 1221*
         1100  -> 1122
2000  -> 2100
         2200

第三个示例需要第二步的光束大小为 3,因为次优光束 1900 和 2100 到 2000 的距离为 100:

1000  -> 1900  -> 1901
         1100
2000* -> 2000* -> 2019*
         2100     2109

注意:我在所有示例中都加入了 3. 和 4. 步骤。

数字X = 1992Y = 2000是一个有趣的例子

1000  -> 1900  -> 1992*
         1200
2000* -> 2100  -> 2199
         2900

因为最佳光束在计算过程中会发生变化。

我写了一个python小程序来演示:

import sys

X = sys.argv[1]
Y = int(sys.argv[2])

def remove(s, i):
    return s[:i] + s[i+1:]

def expand(t):
    result = set()
    val = t[0]
    chars = t[1]
    index = len(val) - len(chars)
    for i in range(len(chars)):
        s = val[:index] + chars[i]
        r = remove(chars, i)
        if index < len(val):
            s += val[index + 1:]
        result.add((s, r))
    return result

beams = [("0" * len(X), X)]
for i in range(len(X)):
    newBeams = set()
    for t in beams:
        newBeams.update(expand(t))
    beams = sorted(newBeams, key = lambda t: abs(Y - int(t[0])))[:3]
    print beams

print "Result:", beams[0][0]

代码不是最优的,但是这个算法有多项式运行时间,O(n² ln n),这个估计非常大方。

【讨论】:

  • @MBo:我改变了方法。
  • 我无法理解整个逻辑。您能否用您的方法解释一个示例,以便我可以尝试为其他测试用例进行模拟。谢谢。 @macmoonshine
  • 我已经更新了我的描述。该算法有点难以描述。
  • 我理解你的算法,除了这部分,“如果梁中两个条目的距离相等,则只需要三个的情况。”。你说的这条线是什么意思?这方面的任何例子。
  • 第三个例子见第二步。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 2021-08-09
  • 2013-09-12
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 1970-01-01
相关资源
最近更新 更多