python 如何检查所有列表元素的最小差值为x

uelo1irk  于 2023-02-11  发布在  Python
关注(0)|答案(5)|浏览(133)

我目前正在尝试迭代一个小的整数列表,并更新任何不满足绝对差条件的值,目的是在多个小列表上使用这个方法,作为一个更大的for循环的一部分。
我有以下清单:

y_list = [16, 29, 10]

此列表需要满足两个条件:
1.没有两个数字是相同的以及
1.每个数字应至少相差10
如果这些条件中的任何一个都不满足,则应调整数量,使其至少相差10。例如:
y_list[0]y_list[1]进行比较:它满足这两个条件并继续前进。
y_list[0]y_list[2]进行比较:它不满足条件2,并加上10减去现有差值。
y_list[1]y_list[0]进行比较:现在这两个条件都不满足了,但它没有调整y_list[0],而是将y_list[1]增加10减去差值。
到目前为止,我已经编写了下面的代码,其中没有考虑到上面示例的最后一个元素,print语句不是必需的,但我只是使用它们来帮助我确保循环的不同部分是否被触发:

for i in range(len(y_list)):
    print(f'Iteration {i}')
    print(f'Old y_list: {y_list}')
    for idx, value in enumerate(y_list):
        difference = abs(value - y_list[i])
        if value != y_list[i]:
            print(f'Comparing {y_list[idx]} with {y_list[i]}')
            print(f'Difference of {difference}')
            if difference < 10:
                print(f'Updating {y_list[idx]}\n')
                y_list[idx] += 10 - difference
        else:
            continue
        print()
    print(f'New list{y_list}\n')

这给了我一个更新的列表,但显然它只在整个列表上迭代三轮。

Output:

Iteration 0
Old y_list: [16, 29, 10]
Comparing 29 with 16
Difference of 13

Comparing 10 with 16
Difference of 6
Updating 10

New list[16, 29, 14]

Iteration 1
Old y_list: [16, 29, 14]
Comparing 16 with 29
Difference of 13

Comparing 14 with 29
Difference of 15

New list[16, 29, 14]

Iteration 2
Old y_list: [16, 29, 14]
Comparing 16 with 14
Difference of 2
Updating 16

Comparing 29 with 14
Difference of 15

New list[24, 29, 14]

我尝试在第二个for循环之前使用while True循环来继续迭代,但没有成功。
我见过all()函数和itertools.takewhile()满足条件的例子,但还没有能够让其中任何一个函数与while循环一起工作。
任何帮助都非常感谢!

ki0zmccv

ki0zmccv1#

解决方案的要求并不完全清楚,因此我做了一些额外的假设:
1.列表中最低的数字不作调整
1.不得向下调整数字
1.对于相等的数字,调整哪个数字无关紧要(如果需要多次调整,则按哪个顺序调整数字)
1.除3外,应保留编号顺序
1.考虑到以上所有因素,调整应该是最小的。注意,忽略规则1和规则2可能会实现较低的调整,但这将使算法复杂得多。
鉴于上述情况,最好的方法是对排序的数据进行处理,即首先比较最小值与第二小值,然后比较第二小值与第三小值,依此类推。pairwise from itertools(从Python 3.10开始提供)非常适合进行这种迭代,对于旧版本的python,你可以使用zip来创建类似的东西。使用pairwise,检查可以归结为一个简单的:

def check(l):
    return all(x2-x1 >= 10 for x1,x2 in itertools.pairwise(sorted(l)))

对于较早的python版本,在排序项上使用zip

def check2(l):
    sl = sorted(l)
    return all(x2-x1 >= 10 for x1,x2 in zip(sl, sl[1:]))

为了调整列表,你也可以使用pairwise,但是当你正在动态地改变列表时,你需要使用索引来做任何改变,enumerate提供了这些:

def adjust(l):
    for (idx1,num1), (idx2,num2) in itertools.pairwise(sorted(enumerate(l), key=lambda tup: tup[1])):
        if l[idx2] < l[idx1]+10: # Note: Don't be tempted to use num1 or num2 here
            l[idx2] = l[idx1]+10
mwngjboj

mwngjboj2#

另一个镜头:
你可以对输入数组进行排序(记住原始索引),确保值之间的差异至少为10,然后将值按原始顺序放回:

y_list = [16, 29, 10]

x = sorted((v, i) for i, v in enumerate(y_list))

out = [x[0]]
for v, i in x[1:]:
    how_much = v - out[-1][0]
    if how_much < 10:
        v += 10 - how_much
    out.append((v, i))

out = [v for v, _ in sorted(out, key=lambda v: v[1])]
print(out)

图纸:

[20, 30, 10]
qeeaahzv

qeeaahzv3#

如果你不在乎维持秩序,我会这么做:

y_list = [16, 29, 10, 50]
y_list.sort()
for i in range(1, len(y_list)):
    y_list[i] += max(0, 10 - (y_list[i] - y_list[i-1]))
print(y_list)

给你:

[10, 20, 30, 50]

如果你真的关心排序,那么@andrej-kesely的答案在我看来是个赢家。

xzv2uavs

xzv2uavs4#

itertools中的accumulate函数可以使这一过程变得非常简单,因为它将遍历列表,处理最后一个值和当前值:

from itertools import accumulate

def adjust(L,diff=10):
    return [*accumulate(L,lambda a,b:max(b,a+diff))]

print(adjust([16, 29, 10])) # [16, 29, 39]

print(adjust([16, 29, 10, 35, 60, 67])) # [16, 29, 39, 49, 60, 70]

如果不允许使用库,可以使用extend方法获得类似的结果:

def adjust(L,diff=10):
    result = L[:1]
    result.extend(max(v,result[-1]+diff) for v in L[1:])
    return result

如果需要在同一个列表示例中“就地”更改值,enumerate()可以帮助遍历和索引:

for i,v in enumerate(y_list[1:]):
    y_list[i+1] = max(v,y_list[i]+10)
  • 请注意,我没有考虑条件#1,因为当条件#2满足时,它永远不会发生。而且,这种方法假设您不试图最小化更改的数量 *

[EDIT]下面是一个流程示例,该流程锚定最小的元素,以保持该元素最小的方式前后工作(即最小限度地调整其周围的其他元素):

y_list = [16, 29, 16, 10, 35, 60, 67]

anchor = y_list.index(min(y_list)) # anchor on smallest
for i,v in enumerate(y_list[anchor+1:],anchor):                     # forward
    y_list[i+1] = y_list[i]+10 if abs(v-y_list[i])<10 else v
for i,v in enumerate(reversed(y_list[:anchor]),len(y_list)-anchor): # backward
    y_list[-i-1] = y_list[-i]+10 if abs(v-y_list[-i])<10 else v
    
print(y_list)
# [16, 30, 20, 10, 35, 60, 70]
dbf7pr2w

dbf7pr2w5#

创建一个函数来计算列表是否满足条件,然后使用一个while循环来运行,直到该函数返回True为止。技巧是使用更紧凑的代码来检查唯一性并进行所有比较。对于唯一性,我们可以只比较集合和列表,如果长度匹配,则所有元素都是唯一的。对于比较,我们使用itertools.combinations来生成所有索引对。如果遇到任何失败条件,我们立即返回False。如果它通过了检查,我们最终返回True。

from itertools import combinations

y_list = [16, 29, 10]

def satisfies_conditions(ys):
    unique = len(set(ys)) == len(ys)
    
    if not unique:
        return False
    
    for i, j in combinations(range(len(ys)), 2):
        diff = abs(ys[i] - ys[j])
        if diff < 10:
            return False
        
    return True

while not satisfies_conditions(y_list):
    for i in range(len(y_list)):
        print(f'Iteration {i}')
        print(f'Old y_list: {y_list}')
        for idx, value in enumerate(y_list):
            difference = abs(value - y_list[i])
            if value != y_list[i]:
                print(f'Comparing {y_list[idx]} with {y_list[i]}')
                print(f'Difference of {difference}')
                if difference < 10:
                    print(f'Updating {y_list[idx]}\n')
                    y_list[idx] += 10 - difference
            else:
                continue
            print()
        print(f'New list{y_list}\n')

相关问题