算法的正确性和逻辑性:最小步骤为1

时间:2015-05-04 11:58:43

标签: c++ algorithm logic correctness

问题陈述:

在正整数上,您可以执行以下3个步骤中的任何一个。

  1. 从中减去1。 (n = n - 1)

  2. 如果它可被2整除,则除以2.(如果n%2 == 0,则n = n / 2)

  3. 如果它可被3整除,则除以3.(如果n%3 == 0,则n = n / 3)

  4. 给定正整数n并且您的任务是找到将n转换为1的最小步数。

    我的递归解决方案(在C ++中)比较N可被3整除的所有3种情况,而一般解决方案仅比较2,但仍然给出正确的解决方案。

    require Module; Module->import(qw(f1 f3))

    但一般的解决方案是,

    int min_steps(int N){ 
            if(N==1) return 0;    
            else{
                    if(N%3==0){
                           if(N%2==0) 
                              return (1+min(min_steps(N/3),min_steps(N/2),min_steps(N-1)));
                           else
                              return(1+min(min_steps(N/3),min_steps(N-1)));
                    }
                    else if(N%2==0){
                            return(1+min(min_steps(N/2),min_steps(N-1)));
                    }
                    else
                            return(1+min_steps(N-1));
            }
    }
    

    我的问题是,为什么我们不比较所有3个案例,但仍然得出正确的解决方案。我无法遵循通用解决方案的算法。任何让我理解的帮助都会受到极大的赞赏。

1 个答案:

答案 0 :(得分:6)

"一般解决方案"是不正确的。有时候除以2然后减去1是最优的,而通用解法代码并不允许这样做。

"一般解决方案"产生不正确的结果642。

642 -> 214 -> 107 -> 106 -> 53 -> 52 -> 26 -> 13 -> 12 -> 4 -> 2 -> 1

然而,这是最佳的,只有一个:

642 -> 321 -> 320 -> 160 -> 80 -> 40 -> 20 -> 10 -> 9 -> 3 -> 1

你可以看到一般解决方案从除以3开始,最佳解决方案首先除以2然后减去1 ......这正是被删除的情况。

虽然它与您的问题没有直接关系,但这里是我用来查找反例的代码(尽管我写完以后已经大大整理了)。它使用您提供的两种算法,但会记住它们以指数速度增加。它还使用了从min_steps返回两个结果的技巧:不仅是最短路径的长度,而且是该路径的第一步。这使得在不编写额外代码的情况下重建路径非常方便。

def memoize(f):
    """Simple memoization decorator"""
    def mf(n, div2, cache={}):
        if (n, div2) not in cache:
            cache[n, div2] = f(n, div2)
        return cache[(n, div2)]
    return mf

@memoize
def min_steps(n, div2):
    """Returns the number of steps and the next number in the solution.

    If div2 is false, the function doesn't consider solutions
    which involve dividing n by 2 if n is divisible by 3.
    """
    if n == 1:
        return 0, None
    best = min_steps(n - 1, div2)[0] + 1, n-1
    if n % 3 == 0:
        best = min(best, (min_steps(n // 3, div2)[0] + 1, n//3))
    if n % 2 == 0 and (div2 or n%3):
        best = min(best, (min_steps(n // 2, div2)[0] + 1, n//2))
    return best

def path(n, div2):
    """Generates an optimal path starting from n.

    The argument div2 has the same meaning as in min_steps.
    """
    while n:
        yield n
        _, n = min_steps(n, div2)

# Search for values of n for which the two methods of finding
# an optimal path give different results.
for i in xrange(1, 1000):
    ms1, _ = min_steps(i, True)
    ms2, _ = min_steps(i, False)
    if ms1 != ms2:
        print i, ms1, ms2
        print ' -> '.join(map(str, path(i, True)))
        print ' -> '.join(map(str, path(i, False)))

这是输出,包括运行时间:

$ time python minsteps.py 
642 10 11
642 -> 321 -> 320 -> 160 -> 80 -> 40 -> 20 -> 10 -> 9 -> 3 -> 1
642 -> 214 -> 107 -> 106 -> 53 -> 52 -> 26 -> 13 -> 12 -> 4 -> 2 -> 1
643 11 12
643 -> 642 -> 321 -> 320 -> 160 -> 80 -> 40 -> 20 -> 10 -> 9 -> 3 -> 1
643 -> 642 -> 214 -> 107 -> 106 -> 53 -> 52 -> 26 -> 13 -> 12 -> 4 -> 2 -> 1

real    0m0.009s
user    0m0.009s
sys 0m0.000s