Python 将一个整数转换为另一个整数的最小步骤数
最近我遇到了一个问题,给定2个整数a和B,我们需要以最少的步骤将a转换为B。 我们可以在计算机上执行以下操作: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
- 如果A为奇数,则减少1
- 如果A为偶数,则增加1
- 将A(偶数或奇数)乘以2
- 如果A是偶数,则除以2
编辑:A不一定小于B基本上您有以下操作:
A==0
,您将如何构造B
?向右,逐个翻转低位并将数字向左移位,例如,如果B==5
,即0x0101,则需要两次翻转和两次移位
现在,我们必须处理A!=0
——在这种情况下,您必须将低位调到0
,然后右移以清除混乱。例如,如果您有A==32
,即0x0100000,并且希望得到5(0x0101),则必须向右移动三次,然后翻转较低的位,就完成了
因此,您所要做的就是:
好的,几个小时过去了,这是解决方案。首先是一个有用的函数,表示创建一个数字需要多少个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
就这些,伙计们!=) 可以使用动态规划优化此问题 我在编写以下代码时考虑了几点:
dp
维护每个状态(A,B)的答案,mapiterationscont
维护调用相同状态(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位”有点不对劲,我来看看