Python 将一个整数转换为另一个整数的最小步骤数

Python 将一个整数转换为另一个整数的最小步骤数,python,algorithm,bit-manipulation,bitwise-operators,Python,Algorithm,Bit Manipulation,Bitwise Operators,最近我遇到了一个问题,给定2个整数a和B,我们需要以最少的步骤将a转换为B。 我们可以在计算机上执行以下操作: 如果A为奇数,则减少1 如果A为偶数,则增加1 将A(偶数或奇数)乘以2 如果A是偶数,则除以2 同样,我们必须找到将A转换为B所需的最小步骤数 约束条件为0(80->100)->(160100)-> 这是通过将每个状态的计数保存到映射中,并为相同的DP状态定义最大递归调用限制(在下面的程序中为3)来实现的 mapdp维护每个状态(A,B)的答案,mapiterationscont

最近我遇到了一个问题,给定2个整数a和B,我们需要以最少的步骤将a转换为B。 我们可以在计算机上执行以下操作:

  • 如果A为奇数,则减少1
  • 如果A为偶数,则增加1
  • 将A(偶数或奇数)乘以2
  • 如果A是偶数,则除以2
同样,我们必须找到将A转换为B所需的最小步骤数

约束条件为0 我的做法: 我尝试使用广度优先搜索来解决这个问题,将一个步骤中所有可能到达的数字添加到一个队列中,但在更高的约束条件下(即超时)失败了

有人能提出一个更快的替代方案吗


编辑:A不一定小于B

基本上您有以下操作:

  • 翻转最低位
  • 将位向左或向右移位
  • 假设您有
    A==0
    ,您将如何构造
    B
    ?向右,逐个翻转低位并将数字向左移位,例如,如果
    B==5
    ,即0x0101,则需要两次翻转和两次移位

    现在,我们必须处理
    A!=0
    ——在这种情况下,您必须将低位调到
    0
    ,然后右移以清除混乱。例如,如果您有
    A==32
    ,即0x0100000,并且希望得到5(0x0101),则必须向右移动三次,然后翻转较低的位,就完成了

    因此,您所要做的就是:

  • 计算在A的最高位等于B的最高位之前必须进行的翻转/r移位次数
  • 然后数一数你需要多少次翻转/换班来清理剩下的部分
  • 计算重建B的下部需要翻转/左移多少次

  • 好的,几个小时过去了,这是解决方案。首先是一个有用的函数,表示创建一个数字需要多少个OP:

    def bit_count(num) :
        # the number of non-zero bits in a number
        return bin(num).count('1')
    
    def num_ops(num) :
        # number of shifts + number of flips
        return num.bit_length() + bit_count(num)
    
    现在,假设A>B,否则我们可以交换它们,同时保持操作数不变。下面是我们必须将
    A
    移动多远才能使其从与
    B
    相同的位开始:

    needed_shifts = A.bit_length() - B.bit_length()
    
    A >>= needed_shifts
    clean_shifts = (A & ~B).bit_length()
    clean_flips = bit_count(A & ~B)
    rebuild_shifts = (B & ~A).bit_length()
    rebuild.flips = bit_count(B & ~A)
    
    在执行此操作时,我们需要翻转一些位:

    mask = (1 << (needed_shifts+1)) - 1
    needed_flips = bit_count(A & mask)
    
    最后,我们总结如下:

    result_ops = needed_shifts + needed_flips + max(clean_shifts,rebuild_shifts) * 2 + clean_flips + rebuils_flips
    

    就这些,伙计们!=)

    可以使用动态规划优化此问题

    我在编写以下代码时考虑了几点:

  • 通过设置基本条件,应小心避免无限递归。对于例如:如果A=0和B(160100)->(80->100)->(160100)->

  • 这是通过将每个状态的计数保存到映射中,并为相同的DP状态定义最大递归调用限制(在下面的程序中为3)来实现的

    map
    dp
    维护每个状态(A,B)的答案,map
    iterationscont
    维护调用相同状态
    (A,B)
    的次数

    请查看以下实现:

    #include <utility> 
    #include <iterator> 
    #include <map>
    #include <set> 
    #include <iostream>
    #include <climits>
    
    typedef long long int LL;
    
    std::map<std::pair<LL, LL>, LL> dp;
    
    std::map<std::pair<LL, LL>, int > iterationsCount;
    
    LL IMPOSSIBLE = (LL)1e9;
    
    LL MAX_RECURSION_LIMIT = 3;
    
    LL convert(LL a, LL b) 
    { 
    //std::cout<<a<<" "<<b<<std::endl;
    
        // To avoid infinite recursion:
            if(iterationsCount.find(std::make_pair(a, b))!=iterationsCount.end() &&
            iterationsCount[std::make_pair(a,b)] > MAX_RECURSION_LIMIT &&
            dp.find(std::make_pair(a,b))==dp.end()){
                return IMPOSSIBLE;
            }
    
        // Maintaining count of each state(A, B)
            iterationsCount[std::make_pair(a, b)]++;
    
        LL value1, value2, value3, value4, value5;
    
        value1 = value2 = value3 = value4 = value5 = IMPOSSIBLE;
    
        if(dp.find(std::make_pair(a,b)) != dp.end()){
            return dp[std::make_pair(a, b)];
        }
    
        // Base Case 
        if(a==0 && b<0){
            return IMPOSSIBLE;
        }
    
        // Base Case 
        if (a == b) 
            return 0; 
    
        //Conditions
        if (a%2 == 1){
    
            if(a < b){
    
                value1 = 1 + convert(2*a, b);
    
            }
    
            else if(a > b){
    
                value2 = 1 + convert(a-1, b); 
    
            }
        }
        else{
    
            if(a < b){
    
    
                value3 = 1 + convert(a*2, b);
    
    
    
                value4 = 1 + convert(a+1, b);
    
            }
    
            else if(a > b){
    
                value5 = 1 + convert(a/2, b);
    
            }
        }
    
        LL ans = std::min(value1, std::min(value2, std::min(value3, std::min(value4, value5))));
    
        dp[std::make_pair(a, b)] = ans;
    
        return ans;
    } 
    
    int main(){
    
        LL ans = convert(10, 95);
        if(ans == IMPOSSIBLE){
            std::cout<<"Impossible";
        }else{
            std::cout<<ans;
        }
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    typedef long long int LL;
    std::map-dp;
    映射迭代计数;
    LL不可能=(LL)1e9;
    LL最大递归极限=3;
    LL转换(LL a、LL b)
    { 
    
    //std::cout可用操作的列表是对称的:两组操作,每个操作相对:

    • 最后一位可以翻转
    • 如果低位为
      0
      ,则数字可以向左移动一个位置或向右移动一个位置
    因此,从
    A
    B
    或从
    B
    A
    需要相同数量的操作

    A
    B
    最多需要从
    A
    0
    的操作数,再加上从
    B
    0
    的操作数。这些操作严格地降低了
    A
    B
    的值。如果在此过程中,可以从B获得中间值无论是
    A
    还是
    B
    ,都不需要一直到
    0

    下面是一个简单的函数,它执行
    a
    B
    上的各个步骤,并在找到此公用编号后立即停止:

    def num_ops(a,b):
    #计算将a转换为b的操作数
    #根据对称性,将b转换为a需要相同数量的OP
    计数=0
    而a!=b:
    如果a>b:
    如果(a&1)!=0:
    a-=1
    其他:
    a>>=1
    其他:
    如果(b&1)!=0:
    b-=1
    其他:
    b>>=1
    计数+=1
    返回计数
    
    想想二进制中的数字,想想这些操作对数字的二进制表示有什么影响……并且只使用一种编程语言来解决这个问题。你能给我一些参考链接吗problem@deHaar很抱歉,为什么我不能使用多个?我知道所有3个,无论哪一个实现更容易,我都知道我会去考虑它的标签,而不是或。你已经有效地要求用java、python和C++编写的程序。不是你想要的。@甜心用解决方案代码(Python)更新了答案。恐怕清理
    A
    和重建
    B
    所需的步骤数计算错误:它只取决于
    A
    的低阶位,而不管
    B
    的对应位如何,类似地,重建操作应该只取决于
    B
    的低阶位。例如,转换
    0b101
    0b111
    应该采取
    0+1+num_ops(0b000)+num_ops(0b010)
    ->
    4
    但我不知道如何改进这5步解决方案:dec,shr,inc,shl,inc.@chqrlie我同意这些步骤的计算错误,但我的错误有点不同。你所说的事情,“不管对应的B位”有点不对劲,我来看看