例如,给定列表[1, 0, 1]
,代码将返回[1,1,0]
。其他例子:
[1,1,1] -- > [1,0,0,0]
[1,0,0,1] --> [1,0,1,0]
我很难理解递归的基本情况,以及如何实现(n-1)情况。
def increment_helper(number):
newNum = []
if len(number) ==1:
if number[0] == 1:
carry = 1
newNum.append(0)
else:
carry = 0
newNum.append(1)
else:
return increment_helper(number-1)
return newNum
所以我确定这里有很多错误,特别是我如何调用我的递归,因为我不知道如何在存储以某种方式删除的数字时在列表中递归。 else return语句显然是不正确的,但我将其用作占位符。我不确定使用什么条件作为增量的基本情况。我想我应该使用一个随身携带变量来跟踪我是否携带了一个,但除此之外,我仍然坚持如何继续。
答案 0 :(得分:0)
这个"棘手"因为你在每一步都要做两件事:
这导致了4个案例。
还有额外的"案例"我们甚至得到了一份清单,但它并不那么有趣。
所以我将其描述如下:
if not carry:
# technically meaningless so just return the entire list immediately
return list
# we have "carry", things will change
if not list:
# assumes [] == [0]
return [1]
if list[0]:
# step on `1` (make it 0 and carry)
return [0] + increment(rest_of_list)
# step on `0` (make it 1 and no more carry)
return [1] + rest_of_list
我强烈建议将列表更改为元组并使用它们。
另请注意,递归位于反转列表中,因此请按以下方式应用:
def increment_helper(lst, carry):
if not carry:
return lst
if not lst:
return [1]
if lst[0]:
return [0] + increment_helper(lst[1:], True)
return [1] + lst[1:]
def increment(lst):
# long but really just reverse input and output
return increment_helper(lst[::-1], True)[::-1]
我通过立即返回列表(短路)使用了一些快捷方式,但是你可以使它更多"纯粹"通过进行递归甚至没有携带。
答案 1 :(得分:0)
你可以通过从尾到头遍历数字来递归地处理(二进制)数字(就像加法一样)。
在执行递归之前,您必须检查两个特殊情况:
对于其余情况,您可以递增当前数字并以递归方式处理当前数字之前的所有数字。
def bin_incr(n, incr=True):
if not incr:
return n
if len(n) == 1:
return [1, 0] if n[0] == 1 else [1]
return (
# `n[-1] == 1` denotes an overflow to the next digit.
bin_incr(n[:-1], n[-1] == 1)
+ [(n[-1] + 1) % 2]
)
答案 2 :(得分:0)
基本情况:我怎么知道我什么时候完成?
当你用完数字时你已经完成了。 number
应该是个别数字的列表;检查其长度以确定何时不重复。
递归案例:下一步是什么?
递归的一般概念是"做一些简单的事情,少量减少问题,并用较小的问题重复出现。"你在这部分的工作是做一位数的加法。如果你需要坚持下去(这个数字是否有一个进位?),那么就重复一遍。否则,您将获得完成所需的所有信息。
具体应用
您的递归步骤将涉及调用increment_helper
少一位数:不是number - 1
,而是number[:-1]
。
从每次递归返回后,您然后想要追加刚刚完成的数字。例如,如果您正在递增1101
,则您的第一个电话会看到右侧的电话会增加一个进位。新数字为0
,您必须重复。暂时抓住0
,用110
给自己打电话,然后获得该通话的结果。将保存的0
附加到该页面,然后返回主程序。
这会让你感动吗?
答案 3 :(得分:0)
最好使用两个函数:一个用于检查最后一个位置的简单增量是否足够,另一个用于在前一次尝试失败时执行递归:
vals = [[1, 0, 1], [1,1,1], [1,0,0,1]]
def update_full(d):
if all(i in [1, 0] for i in d):
return d
start = [i for i, a in enumerate(d) if a > 1]
return update_full([1]+[0 if i > 1 else i for i in d] if not start[0] else [a+1 if i == start[0] -1 else 0 if i == start[0] else a for i, a in enumerate(d)])
def increment(d):
if not d[-1]:
return d[:-1]+[1]
return update_full(d[:-1]+[2])
print(list(map(increment, vals)))
输出:
[[1, 1, 0], [1, 0, 0, 0], [1, 0, 1, 0]]
答案 4 :(得分:0)
试试这个:
def add1(listNum):
if listNum.count(0):
oneArr = [[0] * (len(listNum) - 1)] + [1]
sumArr = []
for i in range(len(listNum)):
sumArr.append(sum(listNum[i], oneArr[i]))
newArr = []
for j in range(len(sumArr) - 1):
if sumArr[len(sumArr) - 1 - j] < 2:
newArr.insert(0, sumArr[len(sumArr) - 1 - j])
else:
newArr.insert(0, 1)
sumArr[len(sumArr) - 1 - j] += 1
return sumArr
else:
return [1] + [[0] * len(listNum)]
对于像这样简单的程序使用递归没有多少理由,这就是我选择提供非递归解决方案的原因。
如果你感兴趣的话,我已经计算了这个函数的时间复杂度,它是O(n)。
答案 5 :(得分:0)
另一种递归方法。
当前输入是否为空列表?
[1]
列表中最后一个元素的总和(value
)和1是否大于1?
number_list[:-1]
)并将[0]
附加到结果中。返回number_list
<强>代码强>:
def increment_helper(number_list):
if not number_list:
return [1]
value = number_list[-1] + 1
if value > 1:
number_list = increment_helper(number_list[:-1]) + [0]
else:
number_list[-1] = value
return number_list
示例输出:
numbers = [[1, 0, 1], [1,1,1], [1,0,0,1]]
for n in numbers:
print("%r ---> %r" % (n, increment_helper(n)))
#[1, 0, 1] ---> [1, 1, 0]
#[1, 1, 1] ---> [1, 0, 0, 0]
#[1, 0, 0, 1] ---> [1, 0, 1, 0]
答案 6 :(得分:0)
我知道你不想使用十进制加法,但是如果你必须使用big endian bit order,那么首先转换回十进制可能是最实用的。否则,您最终会遇到不必要的reverse
调用或尴尬的负数组索引
def binary (dec): # big endian bit order
if dec < 2:
return [ dec ]
else:
return binary (dec >> 1) + [ dec & 1 ]
def decimal (bin, acc = 0):
if not bin:
return acc
else:
return decimal (bin[1:], (acc << 1) + bin[0])
def increment (bin):
# sneaky cheat
return binary (decimal (bin) + 1)
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [1, 0]
# 2 [1, 0] [1, 1]
# 3 [1, 1] [1, 0, 0]
# 4 [1, 0, 0] [1, 0, 1]
# 5 [1, 0, 1] [1, 1, 0]
# 6 [1, 1, 0] [1, 1, 1]
# 7 [1, 1, 1] [1, 0, 0, 0]
# 8 [1, 0, 0, 0] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [1, 0, 1, 0]
但是,如果您可以用 little endian位顺序表示二进制数,则情况会发生变化。而不是转换回十进制,increment
可以直接定义为一个漂亮的递归函数
def binary (dec): # little endian bit order
if dec < 2:
return [ dec ]
else:
return [ dec & 1 ] + binary (dec >> 1)
def increment (bin):
if not bin:
return [1]
elif bin[0] == 0:
return [1] + bin[1:]
else:
return [0] + increment(bin[1:])
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [0, 1]
# 2 [0, 1] [1, 1]
# 3 [1, 1] [0, 0, 1]
# 4 [0, 0, 1] [1, 0, 1]
# 5 [1, 0, 1] [0, 1, 1]
# 6 [0, 1, 1] [1, 1, 1]
# 7 [1, 1, 1] [0, 0, 0, 1]
# 8 [0, 0, 0, 1] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [0, 1, 0, 1]
除此之外:将小端表示转换回十进制有点不同。我提供这个来表明递归的用例存在于任何地方
def decimal (bin, power = 0):
if not bin:
return 0
else:
return (bin[0] << power) + decimal (bin[1:], power + 1)
答案的这一部分为你提供了蛋糕,并允许你吃它。你得到大端序列顺序和一个递归increment
,它按照从左到右的顺序逐步执行 - 你应该使用上面的任何一个实现有很多原因,但是这个目的是为了告诉你,即使你的问题很复杂,它仍然可以递归地思考它。在制作此功能时,没有reverse
或arr[::-1]
被滥用。
def binary (dec): # big endian bit order
if dec < 2:
return [ dec ]
else:
return binary (dec >> 1) + [ dec & 1 ]
def increment (bin, cont = lambda b, carry: [1] + b if carry else b):
if bin == [0]:
return cont ([1], 0)
elif bin == [1]:
return cont ([0], 1)
else:
n, *rest = bin
return increment (rest, lambda b, carry:
cont ([n ^ carry] + b, n & carry))
for x in range (10):
print (x, binary (x), increment (binary (x)))
# 0 [0] [1]
# 1 [1] [1, 0]
# 2 [1, 0] [1, 1]
# 3 [1, 1] [1, 0, 0]
# 4 [1, 0, 0] [1, 0, 1]
# 5 [1, 0, 1] [1, 1, 0]
# 6 [1, 1, 0] [1, 1, 1]
# 7 [1, 1, 1] [1, 0, 0, 0]
# 8 [1, 0, 0, 0] [1, 0, 0, 1]
# 9 [1, 0, 0, 1] [1, 0, 1, 0]
我们首先将问题分解为更小的部分; n
是第一个问题,rest
是其他问题。但是,继续思考的关键(如上面的cont
)就是要大胆思考。
在此特定问题中,n
会根据rest
是否更新而更新。所以我们立即重复rest
并传递一个将继续接收子问题结果的延续。我们的延续会收到子问题b
的答案,以及该子问题是否会产生carry
。
...
else:
n, *rest = bin
return increment (rest, lambda b, carry:
cont ([n ^ carry] + b, n & carry))
n ^ carry
和n & carry
表达式决定了这个子问题的答案是什么以及下一个进位将是什么。以下真值表显示^
和&
分别对answer
和next_carry
进行编码。例如,如果n
为0且carry
为1,则可以使用进位。 answer
将是[1] +
子问题的答案,下一个进位将为0。
n carry (answer, next_carry) n ^ carry n & carry
0 0 ([0] + b, 0) 0 0
0 1 ([1] + b, 0) 1 0
1 0 ([1] + b, 0) 1 0
1 1 ([0] + b, 1) 0 1
基本案例很简单。如果子问题为[0]
,则答案为[1]
,且不会携带0
。如果子问题为[1]
,那么答案为[0]
,并带有1
...
if bin == [0]:
return cont ([1], 0)
elif bin == [1]:
return cont ([0], 1)
最后,设计默认延续 - 如果问题b
的答案导致进位,只需将[1]
添加到答案中,否则只返回答案。
cont = lambda b, carry: [1] + b if carry else b
答案 7 :(得分:0)
您要求生成序列序列的增量/后继/下一个函数。由于其他人已经给出了代码,我将给出开发这些函数的一般方法。
首先,开发一个多次递归(2次或更多次递归调用),用于计算长度为N的所有序列。对于大端序列的二进制序列(bs),从N 0到N 1,基本情况bs(0)表达式是[[]],该序列包含一个没有二进制数字的序列。 bs(n)在bs(n-1)方面的双递归是([0]连接到bs(n-1)的所有成员(按顺序))加([1]接受bs的所有成员( N-1))。
接下来,重点关注相邻递归调用返回的子序列之间的转换。这里只有一个:0,1,...,1到1,0,...,0。要跨越这个边界,我们需要将0后跟0或更多1s替换为1后跟相同的数字0。正如其他人所示,我们通过从右侧扫描前0来找到这样的中断。
事实证明,每个增量越过相邻bs(k)之间的边界调用k的某个值,也就是说,在双递归产生的调用树的某个级别。
到目前为止,据我所知,同样的想法适用于设计格雷码序列的增量函数,组合序列(一次取k个事物)和排列序列。
注1:1-> 0转换可以一次完成1个或一次完成。
注2:二进制位测试和翻转是用于计数+1的图灵机算法。当我记得时,Stephen Wolfram,一种新的科学,在TM章节中提出了3种不同的实现。
注3(在编辑中添加):如果一个从先加0开始先切换到前加1,或从前加到后加,或两者兼而有之,则得到3个其他序列序列,其他3个递增函数。
答案 8 :(得分:0)
在这种情况下,您不需要递归。让我们从最简单的实现开始:
完整的加法器实现是直接的。
def full_adder(a,b,cin):
sum_ = a^(b^cin)
cout = (a&b) | (a|b)&cin
return sum_, cout
进行测试以确保完整的加法器符合规范:
>>> zero_one = (0,1)
>>> [full_adder(*x) for x in [(a,b,c) for a in zero_one for b in zero_one for c in zero_one]]
[(0, 0), (1, 0), (1, 0), (0, 1), (1, 0), (0, 1), (0, 1), (1, 1)]
由于波纹加法器的参数是列表,因此我们需要确保列表长度在加法之前匹配。这是通过将较短的列表填充前导零来完成的。
def ripple_adder(xs,ys):
x, y = map(len, (xs, ys))
alen = max(x, y)
ax, by = map(lambda f: f if len(f) == alen else [0]*(alen-len(f)) + f, (xs, ys))
cout = 0
res = [0]*(alen)
for i in range(alen-1, -1, -1):
a, b, cin = ax[i], by[i], cout
s, cout = full_adder(a, b, cin)
res[i] = s
if cout:
res = [1] + res
return res
最后,我们根据波纹加法器定义bin_inc
二进制增量函数
def bin_inc(bin_lst):
return ripple_adder(bin_lst, [1])
>>> bin_inc([1,1,1])
[1, 0, 0, 0]
>>> bin_inc([1,0,0,1])
[1, 0, 1, 0]
现在需要一个更简单但需要一点洞察力的解决方案。考虑以下
输入xs
的长度L
和输出ys
的观测值
如果xs
全部为1,则ys
的长度为L+1
,ys
的第一个元素为1
,其余的为为零。
如果xs
的元素为零,则使p
为xs
中最后一个零元素的索引。
然后,ys
将是由xs的前p
个元素,后跟一个1
和后跟长度为L - p
的零组成的列表。那是
ys = xs[:p] + [1] + [0]*(L-p)
。
我们可以轻松地将其翻译为python代码。我们使用python的list.index
方法通过处理列表的反面来找到最后出现的零,并适当地调整算法:
def bin_inc_2(xs):
if all(xs):
return [1] + [0]*len(xs)
p = xs[::-1].index(0)
return xs[:-p-1] + [1] + [0]*p
这比基于ripple_adder的实现有效且简单。您可能会注意到的一个小缺点是,当xs
的元素为零时,我们遍历该元素以检查其是否全部为1,然后再次遍历以找到第一个出现的零。
通过使用try except
块,我们可以简化实现并同时使其更具有Python风格:
def bin_inc_3(bin_lst):
try:
p = bin_lst[::-1].index(0)
return bin_lst[:-p-1] + [1] + [0]*p
except ValueError:
return [1] + [0]*len(bin_lst)
就源文本和惯用python而言,此实现很简单。现在,我们针对基于涟漪_adder的加法器对其进行测试,以确保其正常工作。
>>> z_n = (0,1)
>>> xs = [[a,b,c,d,e,f,g,h] for a in z_n for b in z_n for c in z_n for d in z_n
for e in z_n for f in z_n for g in z_n for h in z_n ]
>>> print(all(ripple_adder(x, [1]) == bin_inc_3(x) for x in xs))
True
很棒,它可以按预期工作,并且可以正确地处理测试所证明的前导零(每个数字从0
到255
递增)。
答案 9 :(得分:0)
有进位时只需递归:
def f(n):
# Base cases
if not n:
return [1]
if n == [1]:
return [1, 0]
if n[-1] == 0:
return n[:-1] + [1]
if n[-2:] == [0, 1]:
return n[:-2] + [1, 0]
# Recurse
return f(n[:-2]) + [0, 0]
numbers = [[1, 0, 1], [1,1,1], [1,0,0,1], [1, 0, 1, 1]]
for n in numbers:
print n, f(n)