명시 적 목록보다는 발전기를 반환해야합니다.
에서
- 적어도 1 객체 총 N
개체 :
유효한 결과는 다음 조건을 만족해야
- 결과의 개체 순서는 중요하지 않습니다.
,363,210
- 입력 백
- 두 입력 부대
- 유효한 결과가 여분
요구 # 않고 물체를 끌어 (두 입력 잡화 같을 수도 축퇴 경우) 중복 객체를 포함 할 수있는 중복 된 객체를 포함 할 1 명 가정 # 4 의미 number of input bags <= n <= total number of objects in all bags
데이터 구조
- 입력 가방에 중복 값 (가정 # 2)이 허용되므로 set/frozenset을 사용하여 저장할 수 없습니다. 파이썬 목록은이 작업에 적합합니다. 대체 컨테이너는 collections.Counter 일 수 있으며, 일정 시간 멤버십 테스트와 많은 중복이있는 목록에 대한 공간 효율성이 우수합니다.
- 각각의 유효한 조합도 가방입니다.
- 주문 입력 함 목록에 대한 순서는 중요하지 않으므로 입력 함백으로 일반화 할 수 있습니다. 정신을 위 해 나는 그대로 두겠습니다.
파이썬의 내장 된 itertools
모듈을 사용하여 v2.6에서 소개 된 조합을 생성합니다. 이전 버전의 Python을 사용하고 있다면 레시피를 사용하십시오. 이 코드 예제에서는 명확하게하기 위해 생성기를 목록으로 암시 적으로 변환했습니다.
>>> import itertools, collections
>>> input_bags = [Bag([1,2,2,3,5]), Bag([1,4,5,9]), Bag([12])]
>>> output_bag_size = 5
>>> combos = generate_combinations(input_bags, output_bag_size)
>>> combos.next() #an arbitrary example
Bag([1,1,2,4,12])
는 상술 한 바와 같이 문제가 서열의 조합을 생성 파이썬 내장 itertools 모듈 바로 풀수 하나로 감소 될 수 있음을 깨닫는다. 우리가해야 할 유일한 수정은 요구 사항 # 1을 제거하는 것입니다. 줄어든 문제를 해결하려면 가방의 구성 요소를 하나의 가방에 결합하십시오. >>> all_objects = itertools.chain.from_iterable(input_bags)
>>> all_objects
generator that returns [1, 2, 2, 3, 5, 1, 4, 5, 9, 12]
>>> combos = itertools.combinations(all_objects, output_bag_size)
>>> combos
generator that returns [(1,2,2,3,5), (1,2,2,3,1), (1,2,2,3,4), ...]
가 요구 # 1을 복원하기 위해 각각의 유효한 조합 (출력 백), 출력 백 크기가 사용자가 지정한 값에 도달 할 때까지 백 중 각 입력 가방 및 추가 요소 1 개 요소를 포함 할 필요가있다. [각 입력 백에서 요소 1 개]와 [백에서 추가 요소] 사이의 겹침이 무시되면 솔루션은 첫 번째 요소와 가능한 조합의 가능한 조합의 직교 곱입니다. 겹치기를 처리하려면 [가방의 추가 요소]에서 [각 입력 봉투의 요소 1 개]에서 요소를 제거하고 for 루프를 제거합니다.
>>> combos_with_one_element_from_each_bag = itertools.product(*input_bags)
>>> for base_combo in combos_with_one_element_from_each_bag:
>>> all_objects = list(itertools.chain.from_iterable(input_bags))
>>> for seen in base_combo:
>>> all_objects.remove(seen)
>>> all_objects_minus_base_combo = all_objects
>>> for remaining_combo in itertools.combinations(all_objects_minus_base_combo, output_bag_size-len(base_combo)):
>>> yield itertools.chain(base_combo, remaining_combo)
제거 작업은 목록에서 지원되지만 발전기에서는 지원되지 않습니다.all_objects를 메모리에리스트로 저장하지 않으려면 base_combo의 요소를 건너 뛰는 함수를 작성하십시오.
>>> def remove_elements(iterable, elements_to_remove):
>>> elements_to_remove_copy = Bag(elements_to_remove) #create a soft copy
>>> for item in iterable:
>>> if item not in elements_to_remove_copy:
>>> yield item
>>> else:
>>> elements_to_remove_copy.remove(item)
가방 클래스의 구현은 다음과 같습니다
>>> class Bag(collections.Counter):
>>> def __iter__(self):
>>> return self.elements()
>>> def remove(self, item):
>>> self[item] -= 1
>>> if self[item] <= 0:
>>> del self[item]
전체 코드
import itertools, collections
class Bag(collections.Counter):
def __iter__(self):
return self.elements()
def remove(self, item):
self[item] -= 1
if self[item] <= 0:
del self[item]
def __repr__(self):
return 'Bag(%s)' % sorted(self.elements())
def remove_elements(iterable, elements_to_remove):
elements_to_remove_copy = Bag(elements_to_remove) #create a soft copy
for item in iterable:
if item not in elements_to_remove_copy:
yield item
else:
elements_to_remove_copy.remove(item)
def generate_combinations(input_bags, output_bag_size):
combos_with_one_element_from_each_bag = itertools.product(*input_bags)
for base_combo in combos_with_one_element_from_each_bag:
all_objects_minus_base_combo = remove_elements(itertools.chain.from_iterable(input_bags), base_combo)
for remaining_combo in itertools.combinations(all_objects_minus_base_combo, output_bag_size-len(base_combo)):
yield Bag(itertools.chain(base_combo, remaining_combo))
input_bags = [Bag([1,2,2,3,5]), Bag([1,4,5,9]), Bag([12])]
output_bag_size = 5
combos = generate_combinations(input_bags, output_bag_size)
list(combos)
마침 같은 output_bag_size로 (오류 검사 코드를 추가하여이 해제 > = len (input_bags)
이 접근 방식의 적합성은 다음과 같습니다. 1. 유지 보수가 덜 필요한 코드 (즉, itertools) 2. 재귀가 발생하지 않습니다. 파이썬 성능은 호출 스택 높이에 따라 저하되므로 재귀를 최소화하는 것이 좋습니다. 3. 최소 메모리 사용. 이 알고리즘은 입력 백 목록에 의해 소비되는 것 이상의 일정 공간 메모리를 필요로합니다.