python实现将给定列表划分为(等长的/不等长)的元素和大致相等的两个子列表

本文介绍了一种将列表划分为两个子列表的任务划分算法,确保子列表的元素和接近,包括等长与非等长两种情况,并提供了Python实现。

    这是一个挺有意思的问题,之前做题练习的时候就遇上过一个类似的问题,原题记不住了但是大致的意思是说:CPU可以并发执行两个作业,现在给定一堆作业的执行时间让你安排一下,应该怎么划分作业可以使得最后执行的总时间最短,拿到这个问题的时候第一个想到的就是:这就是一个划分列表的问题,可是当时由于水平有限加上时间限制没能够想出来到底应该怎么写,就用了最简单的方法,所有元素即所有任务时间求和之后直接除以任务执行速度的两倍对结果向上取整,最后AC了一半好像,不得不说,测试的数据还是很有特点的,这里又遇到了一个很相似的问题,索性今天就静下心来好好想一下怎么解决

    问题很简单:给定一个列表,元素都是数值类型的有序无序不知道,这个其实无所谓的,需要将这个列表划分为两个等长的子列表使得他们的绝对值之差能够最小化,这个问题仔细一想很简单,可以分析一下:假设最后得到的两个列表元素分别为:

列表A:  a1+a2+....+an

列表B: b1+b2+...+bn

    那么什么情况下会有sum(A)-sum(B)最小呢?

    我们之前都学过等差序列、等比序列相关的内容,这里不需要这些知识,只是借鉴了思想,想一下:我在这里假设我对A、B中的元素已经排好序了,默认升序,那么:是不是只要(b1-a1)+(b2-a2)+...+(bn-an)最小就行了呢?答案是:肯定的!

    想要这样就需要保证每一个(an-bn)都是最小的,这样的话也很简单就是每次保证an和bn分别为数组中最大的两个数字就行了,做法很简单就是最开始将最大的数字放入A中,弹出最大的数字后,将剩余数组中最大的数字加入到B中,同样弹出,接下来的操作需要相反,即将最大的元素放入B中,同样弹出后,将剩余数组最大的元素放入A中同样弹出.......

循环进行下去直至列表为空即可。

    下面是具体的实现:


#!usr/bin/env python
#encoding:utf-8

'''
__Author__:沂水寒城
功能:将给定的列表划分为(等长的)元素和大致相当的两个列表
'''

def test_func1(one_list):
    '''
    将给定的列表划分为等长的元素和大致相当的两个列表
    '''
    num_list=sorted(one_list[:])
    first_list=[]
    second_list=[]
    first_list.append(num_list.pop(-1))
    second_list.append(num_list.pop(-1))
    while len(num_list):
        second_list.append(num_list.pop(-1))
        first_list.append(num_list.pop(-1))
    return first_list, second_list
if __name__ == '__main__':
    list1=[3,6,9,0,2,14,56,89]
    list2=[6,9,23,56,12,3,7,36]
    print '*******************将给定的列表划分为等长的元素和大致相当的两个列表************************'
    print test_func1(list1)
    print test_func1(list2)

结果如下:


*******************将给定的列表划分为等长的元素和大致相当的两个列表************************
([89, 9, 3, 0], [56, 14, 6, 2])
([56, 12, 7, 3], [36, 23, 9, 6])

    接下来需要想一个问题:如果说修改一下题目,变为更实际的问题,因为现实中往往情况并不会这么恰到好处,比如一个任务特别特别大以至于超过了总任务执行时间的一般,那么显然上面的划分并不是最优的,那么就需要考虑改变一些条件了。

    将给定的列表划分为元素和大致相当的两个列表,这里不再要求等长了怎么办呢?其实:方法同源解决的思路是一样的,只是需要在划分完之后加上一下限制修饰的操作即可。在得到两个划分之后的列表之后开始循环的判断,如何元素和较大的列表与元素和较小的列表的差值大于元素和较大列表中的最小元素的时候,需要弹出最小元素加入的元素和较小列表当中,重复此操作,直至不满足条件为止。

    下面是具体的实现:


def test_func2(one_list):
    '''
    将给定的列表划分为元素和大致相当的两个列表
    '''
    num_list=sorted(one_list[:])
    first_list=[]
    second_list=[]
    first_list.append(num_list.pop(-1))
    second_list.append(num_list.pop(-1))
    while len(num_list):
        second_list.append(num_list.pop(-1))
        first_list.append(num_list.pop(-1))
    while sum(first_list)-sum(second_list)>min(first_list):
        second_list.append(first_list.pop(first_list.index(min(first_list))))
    return first_list, second_list


结果如下:


*******************将给定的列表划分为元素和大致相当的两个列表************************
([89], [56, 14, 6, 2, 0, 3, 9])
([56, 12, 7], [36, 23, 9, 6, 3])

    完整的实现如下:


#!usr/bin/env python
#encoding:utf-8

'''
__Author__:沂水寒城
功能:将给定的列表划分为(等长的)元素和大致相当的两个列表
'''

def test_func1(one_list):
    '''
    将给定的列表划分为等长的元素和大致相当的两个列表
    '''
    num_list=sorted(one_list[:])
    first_list=[]
    second_list=[]
    first_list.append(num_list.pop(-1))
    second_list.append(num_list.pop(-1))
    while len(num_list):
        second_list.append(num_list.pop(-1))
        first_list.append(num_list.pop(-1))
    return first_list, second_list
        

def test_func2(one_list):
    '''
    将给定的列表划分为元素和大致相当的两个列表
    '''
    num_list=sorted(one_list[:])
    first_list=[]
    second_list=[]
    first_list.append(num_list.pop(-1))
    second_list.append(num_list.pop(-1))
    while len(num_list):
        second_list.append(num_list.pop(-1))
        first_list.append(num_list.pop(-1))
    while sum(first_list)-sum(second_list)>min(first_list):
        second_list.append(first_list.pop(first_list.index(min(first_list))))
    return first_list, second_list



if __name__ == '__main__':
    list1=[3,6,9,0,2,14,56,89]
    list2=[6,9,23,56,12,3,7,36]
    list3=[1345,234,56,78,90,4,1,56,78,4,456,77]
    print '*******************将给定的列表划分为等长的元素和大致相当的两个列表************************'
    print test_func1(list1)
    print test_func1(list2)
    print test_func1(list3)
    print '*******************将给定的列表划分为元素和大致相当的两个列表************************'
    print test_func2(list1)
    print test_func2(list2)
    print test_func2(list3)


结果如下:


*******************将给定的列表划分为等长的元素和大致相当的两个列表************************
([89, 9, 3, 0], [56, 14, 6, 2])
([56, 12, 7, 3], [36, 23, 9, 6])
([1345, 90, 78, 56, 4, 1], [456, 234, 78, 77, 56, 4])
*******************将给定的列表划分为元素和大致相当的两个列表************************
([89], [56, 14, 6, 2, 0, 3, 9])
([56, 12, 7], [36, 23, 9, 6, 3])
([1345], [456, 234, 78, 77, 56, 4, 1, 4, 56, 78, 90])


    这里需要指出来的是:划分为等长的列表的前提是:列表长度必须是偶数的不然没有意义,当然如果非要使用奇数长度的列表的话可以采用补充“0”的方法使其变为偶数,不影响最后的结果,程序中没有添加这个判断,需要的话可以自行添加。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Together_CZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值