一种替代高阶循环的分组优化方法

  • 背景技术 

我们可能遇到这种需求:把M个人分为{N1,N2,N3,...Nk}共k组,有约束条件(如第i组人数为3人,或者所有组别人数相同),给出优化目标(如使组之间的人员年龄之和差最小),求最优分组方式。

传统的思路会想到for循环。定义M个循环变量,i1,i2,i3,...,iM,在M层循环内部计算优化目标,检验约束条件。但这样做,有以下弊端:

  1. 代码冗长繁琐
  2. 代码容易出错,包括打字错误与逻辑错误
  3. 不易检查代码,不易维护

本文给出一种可以完全替代分组优化情景中的高阶循环的算法。

  • 内容 

首先,把分组结果表示为一个M长度的K进制字符串S,若S的第i位取a,说明第i个人分到第a组(0<=a<K)。每种分组方式的字符串S唯一。

S与数值X一一对应,S即X的K进制字符串表示。对于任意一个X(0<=X<Pow(K,M)),可以把X转化为K进制字符串,并在高位补0,得到M长度的S。

遍历满足约束条件的X的所有可能值,得到所有的S。取出S的每一位,得到分组结果(第i为为m说明第i人分到第m组),计算优化目标,得到最优解。

  • 具体实施方式

以9人分3组,每组人数一致,求年龄和之方差最小值为例。

  1. 遍历准备。9人分3组的所有可能性有Pow(3,9)(3的9次方=19683)种。X遍历0到Pow(3,9)-1的所有值,求对应的3进制字符串S。
  2. 考虑约束条件。遍历S中每个字符,计算出字符0、字符1、字符2的数量,如果数量不等,即不满足约束条件,直接返回,不计算优化值。
  3. 对于满足约束条件的分组方式Sx,遍历Sx中每个字符,若Sx[i]==0, 把第i人的年龄值累加到第0组的年龄和中;若Sx[i]==1, 把第i人的年龄值累加到第1组的年龄和中;若Sx[i]==2, 把第i人的年龄值累加到第2组的年龄和中。计算出该分组方式的方差。若当前值比历史最优值更优,更新最优值。
import math
def to_base3(n, length=9):
    # 转换为三进制字符串
    n = (int)(n)
    if n == 0:
        base3_string = "0"
    else:
        digits = []
        while n:
            digits.append(str(n % 3))
            n //= 3
        base3_string = ''.join(digits[::-1])
    
    # 填充到指定长度
    return base3_string.zfill(length)
def D(x1,x2,x3):
    x1 = (float)(x1)
    x2 = (float)(x2)
    x3 = (float)(x3)
    ave = (x1+x2+x3)/3.0
    return (x1-ave)*(x1-ave)+(x2-ave)*(x2-ave)+(x3-ave)*(x3-ave)
LEN = 9
ID = math.pow(3,9)
group = [1,2,3,4,5,6,7,8,9]
Dmin = 0
result = ""
isFirsttime = True
for i in range((int)(ID)):
    s = to_base3(i)
    cnt0,cnt1,cnt2 = 0,0,0
    list0,list1,list2 = [],[],[]
    for j in range(9):
        c = s[j]
        if(c=='0'):
            cnt0+=1
            list0.append(j)
        if(c=='1'):
            cnt1+=1
            list1.append(j)
        if(c=='2'):
            cnt2+=1
            list2.append(j)
    
    
    if(cnt0==3 and cnt1==3 and cnt2==3):
        x1 = group[list0[0]]+group[list0[1]]+group[list0[2]]
        x2 = group[list1[0]]+group[list1[1]]+group[list1[2]]
        x3 = group[list2[0]]+group[list2[1]]+group[list2[2]]
        d = D(x1,x2,x3)
        if(isFirsttime):
            isFirsttime = False
            Dmin = d
            result = s
        else:
            if(d<Dmin):
                Dmin = d
                result = s
print("group allocation "+result)
print("min D ")
print(Dmin)


运行结果
group allocation 012120201
min D
0.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值