## 2018年12月3日月曜日

### Algorithm - Python - アメリカズ・ゴット・タレント(表の縮小、2次元の表をリストで表す)

コード(Emacs)

Python 3

```#!/usr/bin/env python3

def remove_talent(candidates: list,
candidate_talents: list,
all_talents: list,
talent: str) -> (list, list, str):
'''
>>> remove_talent(candidates, candidate_talents, talents, 'Sing')
(['Aly', 'Bob', 'Cal', 'Don', 'Eve', 'Fay'], [['Flex', 'Code'], ['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code'], ['Act', 'Code']], ['Sing', 'Dance', 'Magic', 'Act', 'Flex', 'Code'], None)
>>> remove_talent(candidates, candidate_talents, talents, 'Flex')
(['Bob', 'Cal', 'Don', 'Eve', 'Fay'], [['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code'], ['Act', 'Code']], ['Sing', 'Dance', 'Magic', 'Act'], 'Aly')
>>> remove_talent(candidates2, candidate_talents2, talents2, 2)
(['A', 'B', 'C', 'D', 'E', 'F', 'G'], [[4, 5, 7], [1, 2, 8], [2, 4, 6, 9], [3, 6, 9], [2, 3, 9], [7, 8, 9], [1, 3, 7]], [1, 2, 3, 4, 5, 6, 7, 8, 9], None)
>>> remove_talent(candidates2, candidate_talents2, talents2, 5)
(['B', 'C', 'D', 'E', 'F', 'G'], [[1, 2, 8], [2, 4, 6, 9], [3, 6, 9], [2, 3, 9], [7, 8, 9], [1, 3, 7]], [1, 2, 3, 6, 8, 9], 'A')
'''
cover = False
new_candidates = candidates[:]
new_candidate_talents = candidate_talents[:]
new_all_talents = all_talents[:]
for i, talents in enumerate(new_candidate_talents):
if talent in talents:
if cover:
return new_candidates, new_candidate_talents, new_all_talents, None
cover = True
talent_index = i
if cover:
candidate = new_candidates.pop(talent_index)
talents = new_candidate_talents.pop(talent_index)
for talent in talents:
if talent in new_all_talents:
new_all_talents.remove(talent)
else:
candidate = None
return new_candidates, new_candidate_talents, new_all_talents, candidate

def remove_talents(candidates: list,
candidate_talents: list,
all_talents: list) -> (list, list, list, list):
i = 0
new_candidates = candidates[:]
new_candidate_talents = candidate_talents[:]
new_all_talents = all_talents[:]
other_candidates = []
while i < len(new_all_talents):
talent = new_all_talents[i]
new_candidates, new_candidate_talents, new_all_talents, candidate = \
remove_talent(new_candidates, new_candidate_talents,
new_all_talents, talent)
if not (candidate is None):
other_candidates.append(candidate)
else:
i += 1
return new_candidates, new_candidate_talents, new_all_talents, other_candidates

def is_cover(talents: list, other_talents: list) -> bool:
for talent in talents:
if talent not in other_talents:
return False
return True

def remove_candidates(candidates, candidate_talents) -> (list, list):
'''
>>> remove_candidates(candidates, candidate_talents)
(['Aly', 'Bob', 'Cal', 'Don', 'Eve'], [['Flex', 'Code'], ['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code']])
'''
new_candidates = []
new_candidate_talents = []
for i, talents in enumerate(candidate_talents):
talents = candidate_talents[i]
cover = False
for j, other_talents in enumerate(candidate_talents):
if i != j and is_cover(talents, other_talents):
cover = True
break
if not cover:
new_candidates.append(candidates[i])
new_candidate_talents.append(talents)

return new_candidates, new_candidate_talents

def is_good(combination: list, candidates: list, candidate_talents: list,
all_talents: list) -> bool:
'''
>>> is_good(['Aly', 'Cal', 'Eve'], candidates, candidate_talents, talents)
True
'''
for talent in all_talents:
cover = False
for candidate in combination:
candidate_talent = candidate_talents[candidates.index(candidate)]
if talent in candidate_talent:
cover = True
break
if not cover:
return False
return True

def hire_for_show(
candidates: list, candidate_talents: list, talents: list) -> None:
'''
>>> hire_for_show(candidates, candidate_talents, talents)
Optimum Solution: ['Aly', 'Cal', 'Eve']
>>> hire_for_show(candidates2, candidate_talents2, talents2)
Optimum Solution: ['A', 'B', 'D']
'''
candidates, candidate_talents = remove_candidates(candidates,
candidate_talents)
candidates, candidate_talents, talents, other_candidates = \
remove_talents(candidates, candidate_talents, talents)
n = len(candidates)
hire = candidates[:]
for i in range(2 ** n):
combination = []
num = i
for j in range(n):
if num % 2 == 1:
combination = [candidates[n - 1 - j]] + combination
num //= 2
if is_good(combination, candidates, candidate_talents, talents) and \
len(hire) > len(combination):
hire = combination
hire += other_candidates
print(f'Optimum Solution: {sorted(hire)}')

if __name__ == '__main__':
import doctest
talents = ['Sing', 'Dance', 'Magic', 'Act', 'Flex', 'Code']
candidates = ['Aly', 'Bob', 'Cal', 'Don', 'Eve', 'Fay']
candidate_talents = [['Flex', 'Code'],
['Dance', 'Magic'],
['Sing', 'Magic'],
['Sing', 'Dance'],
['Dance', 'Act', 'Code'],
['Act', 'Code']]
talents2 = list(range(1, 10))
candidates2 = [chr(ord('A') + i) for i in range(7)]
candidate_talents2 = [[4, 5, 7],
[1, 2, 8],
[2, 4, 6, 9],
[3, 6, 9],
[2, 3, 9],
[7, 8, 9],
[1, 3, 7]]
globs = locals()
globs.update({'talents': talents,
'candidates': candidates,
'candidate_talents': candidate_talents,
'talents2': talents2,
'candidates2': candidates2,
'candidate_talents2': candidate_talents2})
doctest.testmod(globs=globs)
```

```\$ ./sample2.py
Trying:
hire_for_show(candidates, candidate_talents, talents)
Expecting:
Optimum Solution: ['Aly', 'Cal', 'Eve']
ok
Trying:
hire_for_show(candidates2, candidate_talents2, talents2)
Expecting:
Optimum Solution: ['A', 'B', 'D']
ok
Trying:
is_good(['Aly', 'Cal', 'Eve'], candidates, candidate_talents, talents)
Expecting:
True
ok
Trying:
remove_candidates(candidates, candidate_talents)
Expecting:
(['Aly', 'Bob', 'Cal', 'Don', 'Eve'], [['Flex', 'Code'], ['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code']])
ok
Trying:
remove_talent(candidates, candidate_talents, talents, 'Sing')
Expecting:
(['Aly', 'Bob', 'Cal', 'Don', 'Eve', 'Fay'], [['Flex', 'Code'], ['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code'], ['Act', 'Code']], ['Sing', 'Dance', 'Magic', 'Act', 'Flex', 'Code'], None)
ok
Trying:
remove_talent(candidates, candidate_talents, talents, 'Flex')
Expecting:
(['Bob', 'Cal', 'Don', 'Eve', 'Fay'], [['Dance', 'Magic'], ['Sing', 'Magic'], ['Sing', 'Dance'], ['Dance', 'Act', 'Code'], ['Act', 'Code']], ['Sing', 'Dance', 'Magic', 'Act'], 'Aly')
ok
Trying:
remove_talent(candidates2, candidate_talents2, talents2, 2)
Expecting:
(['A', 'B', 'C', 'D', 'E', 'F', 'G'], [[4, 5, 7], [1, 2, 8], [2, 4, 6, 9], [3, 6, 9], [2, 3, 9], [7, 8, 9], [1, 3, 7]], [1, 2, 3, 4, 5, 6, 7, 8, 9], None)
ok
Trying:
remove_talent(candidates2, candidate_talents2, talents2, 5)
Expecting:
(['B', 'C', 'D', 'E', 'F', 'G'], [[1, 2, 8], [2, 4, 6, 9], [3, 6, 9], [2, 3, 9], [7, 8, 9], [1, 3, 7]], [1, 2, 3, 6, 8, 9], 'A')
ok
__main__
__main__.is_cover
__main__.remove_talents
4 items passed all tests:
2 tests in __main__.hire_for_show
1 tests in __main__.is_good
1 tests in __main__.remove_candidates
4 tests in __main__.remove_talent
8 tests in 7 items.
8 passed and 0 failed.
Test passed.
\$
```