最后的人站 - 封闭形式方程

时间:2017-03-08 14:16:34

标签: matlab

我编写了一个matlab代码,以便找到最后一位代表here问题的人。

问题n人们手里拿着枪站成一个圆圈。 1次杀死2次,3次杀死4次,5次杀死6次等等(循环)直到我们只剩下一个人。谁将是最后一个活着的人。

function N = lastManStanding(n)

while length(n) ~= 1
    if mod(length(n),2) == 0
        for ii = 1:length(n)/2
            a(ii) = [ii*2];
        end
        n(a) = [];
        a = [];
    end
    if mod(length(n),2) == 1
        for jj = 1:floor(length(n)/2)
            b(jj) = [jj*2];
        end
        n(b) = [];
        n = circshift(n,[0,1]);
        b = [];        

    end
end

N = n;

这段代码基本上做的是将范围数组(1,....,n)作为输入并返回最后剩下的索引。我想知道是否存在一个封闭的形式方程来解决这个问题,或者是否可以进一步简化上述代码。

更新 找到下面的更新代码

function N = lastManStanding(n)

while length(n) ~= 1
    if mod(length(n),2) == 0
        n(2:2:end) = [];        
    else mod(length(n),2) == 1
        n(2:2:end) = [];
        n = circshift(n,[0,1]);
    end
end

N = n;

3 个答案:

答案 0 :(得分:4)

封闭式解决方案:

如果使用脚本为 n = 1:100绘制“幸存者”索引 i ,您将看到一个锯齿形状的函数,每个函数都会回落到1 n = 2 ^ k ,其中 k 是整数值。 这意味着在那些点 mod(n, 2 ^ k) = 0。

你可以找到2 ^ k 作为 n 2的下一个次要倍数。因此 k 是:

k = floor(log2(n));

在函数的边缘之间 n > 2 ^ k 函数以2 * mod(n, 2 ^ k)上升。由于函数的偏移量为1,因此我们可以将闭合形式的解决方案写为:

i = 2 * mod(n, 2^k) + 1;

或内联:

i = 2 * mod(n, 2.^floor(log2(n))) + 1;

情节:

enter image description here

<强>更新

这个问题也被称为约瑟夫斯问题。您可以在此处找到更一般和数学上严格的推导: https://en.wikipedia.org/wiki/Josephus_problem

答案 1 :(得分:3)

您还可以通过更加数学的方式解决此问题而无需循环:

%for n in [1,inf[
lastManStanding = mod(n,2.^floor((log(n)/log(2))))*2+1;

根据wolfie的建议,您也可以直接使用函数log2

lastManStanding = mod(n,2.^floor(log2(n)))*2+1;

函数mod可以处理向量,因此n可以是向量。

答案 2 :(得分:2)

您的更新代码已损坏,您在end声明之前有else声明。你的代码:

function N = lastManStanding(n)

while length(n) ~= 1
    if mod(length(n),2) == 0
        n(2:2:end) = [];        
    end % <------------------------ PROBLEM!! end before else
    else mod(length(n),2) == 1 % <- PROBLEM!! condition not needed for else
        n(2:2:end) = [];
        n = circshift(n,[0,1]);
    end
end

N = n;

可以通过注意n(2:2:end) = [];ifelse是常见的来进一步简化,因此可以将其拉到外面......

function N = lastManStanding(n)

while length(n) ~= 1
    n(2:2:end) = [];  
    if mod(length(n),2) == 1
        n = circshift(n,[0,1]);
    end
end

N = n;

另外,您实际上可以完全删除if,因为mod的输出是01,这就是您希望转换的数量!< / p>

function N = lastManStanding(n)

while length(n) ~= 1
    shft = mod(length(n),2); % Have to calculate here before n gets changed
    n(2:2:end) = [];  
    n = circshift(n,[0,shft]);
end

N = n;

评论中建议的最后一次优化是根本不使用N。因为n最后只是一个标量,所以使用

function n = lastManStanding(n)

while length(n) ~= 1
    shft = mod(length(n),2); % Have to calculate here before n gets changed
    n(2:2:end) = [];  
    n = circshift(n,[0,shft]);
end

请注意,我没有测试答案是否正确,但结果与您的相同。