Cara efisien untuk menghitung kombinasi panjang tertentu dari data yang berdekatan?

Saya memiliki daftar elemen, yang mana saya ingin menentukan semua kemungkinan kombinasi yang dapat diatur - menjaga urutannya - untuk sampai pada grup 'n'

Jadi sebagai contoh, jika saya memiliki daftar terurut A, B, C, D, E, dan hanya ingin 2 grup, empat solusinya adalah;

ABCD, E
ABC, DE
AB, CDE
A, BCDE

Sekarang, dengan bantuan dari postingan StackOverflow lainnya, saya telah menemukan solusi brute-force yang bisa diterapkan yang menghitung semua kemungkinan kombinasi dari semua kemungkinan pengelompokan yang darinya saya mengekstrak kasus-kasus yang memenuhi jumlah pengelompokan target saya.

Untuk jumlah elemen yang masuk akal, ini baik-baik saja, tetapi ketika saya memperluas jumlah elemen, jumlah kombinasi meningkat dengan sangat cepat, dan saya bertanya-tanya apakah mungkin ada cara cerdas untuk membatasi solusi yang dihitung hanya pada solusi yang memenuhi nomor pengelompokan target saya?

Kode sejauh ini adalah sebagai berikut;

import itertools
import string
import collections

def generate_combination(source, comb):
    res = []
    for x, action in zip(source,comb + (0,)):
        res.append(x)
        if action == 0:
            yield "".join(res)
            res = []
            
#Create a list of first 20 letters of the alphabet
seq = list(string.ascii_uppercase[0:20])
seq

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T']

#Generate all possible combinations
combinations = [list(generate_combination(seq,c)) for c in itertools.product((0,1), repeat=len(seq)-1)]
len(combinations)

524288

#Create a list that counts the number of groups in each solution, 
#and counter to allow easy query
group_counts = [len(i) for i in combinations]
count_dic = collections.Counter(group_counts)
count_dic[1], count_dic[2], count_dic[3], count_dic[4], count_dic[5], count_dic[6]

(1, 19, 171, 969, 3876, 11628)

Jadi seperti yang Anda lihat, meskipun lebih dari setengah juta kombinasi telah dihitung, jika saya hanya menginginkan kombinasi yang panjangnya = 5, hanya 3.876 kebutuhan yang telah dihitung

Ada saran?


person vinomarky    schedule 06.07.2019    source sumber


Jawaban (1)


Partisi seq menjadi 5 bagian setara dengan pilihan 4 lokasi di range(1, len(seq)) untuk memotong seq. Jadi Anda dapat menggunakan itertools.combinations(range(1, len(seq)), 4) untuk membuat semua partisi seq menjadi 5 bagian:

import itertools as IT
import string

def partition_into_n(iterable, n, chain=IT.chain, map=map):
    """
    Return a generator of all partitions of iterable into n parts.
    Based on http://code.activestate.com/recipes/576795/ (Raymond Hettinger)
    which generates all partitions.
    """
    s = iterable if hasattr(iterable, '__getitem__') else tuple(iterable)
    size = len(s)
    first, middle, last = [0], range(1, size), [size]
    getitem = s.__getitem__
    return (map(getitem, map(slice, chain(first, div), chain(div, last)))
            for div in IT.combinations(middle, n-1))

seq = list(string.ascii_uppercase[0:20])
ngroups = 5
for partition in partition_into_n(seq, ngroups):
    print(' '.join([''.join(grp) for grp in partition]))

print(len(list(partition_into_n(seq, ngroups))))

hasil

A B C D EFGHIJKLMNOPQRST
A B C DE FGHIJKLMNOPQRST
A B C DEF GHIJKLMNOPQRST
A B C DEFG HIJKLMNOPQRST
...
ABCDEFGHIJKLMNO P Q RS T
ABCDEFGHIJKLMNO P QR S T
ABCDEFGHIJKLMNO PQ R S T
ABCDEFGHIJKLMNOP Q R S T
3876
person unutbu    schedule 07.07.2019