如何用特定长度的线段填充线,同时保持这些线段之间的比例?

时间:2015-08-18 14:21:52

标签: python python-2.7 split geometry

假设我有一条 n 长的行,并且我有一个或多个具有指定尺寸的线段类型(让我们称之为项目)。使用那些"项目"我需要在尝试维持段之间的指定比率时填充线。必须尽可能地填充线,即使这意味着比率不完全匹配。该生产线不能过满。

SAMPLE INPUT

  

available_length = 15

     

A:尺寸= 2,理想比率= 1/3

     

B:大小= 4,理想比率= 2/3

SAMPLE OUTPUT

  

3 A(2 * 3 = 6)

     B中的2个(4 * 2 = 8)

     

余数= 1(15-14 = 1)

以下是我到目前为止的代码,唯一的问题是我不确定这是否是一个很好的方法来做这个并且我不确定如何填写剩余部分

available_length = 15

item_set = {
    'a': {'length': 2.0, 'fraction': 0.33},
    'b': {'length': 4.0, 'fraction': 0.66},
}

results = {}
for item, item_data in item_set.iteritems():
    results[item] = {}
    ideal_total_length = available_length * item_data['fraction']
    rounded_ideal_total_length = round(ideal_total_length / item_data['length']) * item_data['length']
    possible_multiples = rounded_ideal_total_length / item_data['length']
    results[item].update({
        'length': item_data['length'],
        'fraction': item_data['fraction'],
        'multiples': possible_multiples,
    })

used_length = sum([results[i]['length'] * results[i]['multiples'] for i in results])
remainder = available_length - used_length

print results
print "remainder: ", remainder

2 个答案:

答案 0 :(得分:0)

您只需更新item_set并执行此操作

available_length = 15

item_set = {
    'a': {'length': 2.0, 'fraction': 0.33},
    'b': {'length': 4.0, 'fraction': 0.66},
}

small = available_length
rem = available_length

while rem > 0:
    for k, v in item_set.items():
        small = min(v['length'], small)

        if 'cnt' in v:
            if v['length'] < rem:
                v['cnt'] += 1
        else:
            v['cnt'] = int(available_length * v['fraction'] / v['length'])

    rem = available_length - sum(v['length'] * v['cnt'] for v in tem_set.values())
    if rem == 0 or rem < small:
        break

print rem
print item_set

                                                                                                3,1           Top

答案 1 :(得分:0)

这是我找到的解决方案。我应该注意到,对于整体非常小且开始和结束之间的距离非常大的问题,它不会很好用,但它对我的目的很有用。

from operator import itemgetter
import itertools

def fill_remaining(fill_data_dict, n_from, n_to):
    combo_depth = 5 # this is the limit for number of moves
    if n_from > n_to:
        # if we need to be able to move backwards
        source_dict = {}
        for key, fill_value in fill_data_dict.iteritems():
            pos_key = '[+]' + key
            source_dict[pos_key] = fill_value
            neg_key = '[-]' + key
            neg_data = fill_value * -1
            source_dict[neg_key] = neg_data
        source_dict[None] = None
        move_combo_set = list(itertools.combinations_with_replacement(source_dict, combo_depth))
    else:
        source_dict = {}
        for key, fill_value in fill_data_dict.iteritems():
            pos_key = '[+]' + key
            source_dict[pos_key] = fill_value
        source_dict[None] = None
        move_combo_set = list(itertools.combinations_with_replacement(source_dict, combo_depth))
    # use combo list to find best combinaton
    results = []
    for row_index in xrange(len(move_combo_set)):
        n_sum = n_from
        for i in move_combo_set[row_index]:
            if i:
                n_sum += source_dict[i]
        # diff = result from combo - target number ... so a positive number means we went over
        diff = n_sum - n_to
        moves_number = len(filter(None, move_combo_set[row_index]))
        if diff <= 0:
            results.append([row_index, diff, moves_number])
    # sort to get best result
    rs_2 = sorted(results, key=itemgetter(2)) # sort by moves (secondary)
    rs_1 = sorted(rs_2, key=itemgetter(1), reverse=True) # then by diff (the primary)
    if rs_1:
        best_result_index = rs_1[0][0]
        best_result = filter(None, move_combo_set[best_result_index])
        return best_result
    else:
        return ()

fill_data_dict = {
    'A': 1,
    'B': 3,
}

n_from = 18
n_to = 24

fill = fill_remaining(fill_data_dict, n_from, n_to)
print fill