-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[report] | ||
exclude_lines = | ||
pragma: no cover | ||
if __name__ == .__main__.: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
*.pyc | ||
.vscode | ||
build | ||
partition.egg-info | ||
dist | ||
.tox | ||
.coverage | ||
htmlcov |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.0.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import argparse | ||
|
||
def get_parser(): | ||
|
||
parser = argparse.ArgumentParser() | ||
|
||
parser.add_argument("--numbers", help="integer numbers to be partitioned, seperated by comma") | ||
|
||
parser.add_argument("--grouplen", type=int, help="length of groups to hold the partitioned integer numbers, default is 2", default=2) | ||
|
||
parser.add_argument("--algorithm", help="select partition algorithms, available options are greedy, kk and dp", choices=["greedy", "kk", "dp"]) | ||
|
||
parser.add_argument("--version", action='store_true', help="print version") | ||
|
||
return parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import math | ||
import numpy as np | ||
import sys | ||
|
||
def dp(number_list, group_len=2): | ||
if group_len != 2: | ||
sys.exit("unsupported group length: %s for DP algorithm!" % group_len) | ||
|
||
n = len(number_list) | ||
k = sum(number_list) | ||
s = int(math.floor(k/2)) | ||
p = np.zeros((s+1, n+1)) | ||
p[0] = 1 | ||
for i in range(1, s+1): | ||
for j in range(1, n+1): | ||
if i - number_list[j-1] >= 0: | ||
p[i][j] = p[i][j-1] or p[i-number_list[j-1]][j-1] | ||
else: | ||
p[i][j] = p[i][j-1] | ||
return p[s][n] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
def greedy(number_list, group_len=2): | ||
groups = [[] for i in range(group_len)] | ||
for i in sorted(number_list, reverse=True): | ||
groups.sort(key=lambda x: sum(x)) | ||
groups[0].append(i) | ||
return groups |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import heapq | ||
try: | ||
from Queue import LifoQueue | ||
except ImportError: | ||
from queue import LifoQueue | ||
import sys | ||
|
||
def kk(number_list, group_len=2): # Karmarkar-Karp heuristic | ||
if group_len != 2: | ||
sys.exit("unsupported group length: %s for KK algorithm!" % group_len) | ||
pairs = LifoQueue() | ||
group1, group2 = [], [] | ||
heap = [(-1*i, i) for i in number_list] | ||
heapq.heapify(heap) | ||
while len(heap) > 1: | ||
i, labeli = heapq.heappop(heap) | ||
j, labelj = heapq.heappop(heap) | ||
pairs.put((labeli, labelj)) | ||
heapq.heappush(heap, (i-j, labeli)) | ||
group1.append(heapq.heappop(heap)[1]) | ||
|
||
while not pairs.empty(): | ||
pair = pairs.get() | ||
if pair[0] in group1: | ||
group2.append(pair[1]) | ||
elif pair[0] in group2: | ||
group1.append(pair[1]) | ||
return [group1, group2] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import sys | ||
|
||
import argument | ||
import greedy | ||
import kk | ||
import dp | ||
from . import __version__ | ||
|
||
def partition(args=None): | ||
parser = argument.get_parser() | ||
args = parser.parse_args(args) | ||
if args.version: | ||
print("partition v" + __version__) | ||
return args.version | ||
|
||
number_list = [int(i) for i in args.numbers.split(",")] | ||
|
||
if args.algorithm == "greedy": | ||
groups = greedy.greedy(number_list, args.grouplen) | ||
elif args.algorithm == "kk": | ||
groups = kk.kk(number_list, args.grouplen) | ||
elif args.algorithm == "dp": | ||
#TODO: fix the dp algorithm bug | ||
groups = dp.dp(number_list, args.grouplen) | ||
else: | ||
sys.exit("unsupported partition algorithm: %s" % args.algorithm) | ||
print("Partition %s into %s groups, using algorithm: %s" % (args.numbers, args.grouplen, args.algorithm)) | ||
min_groupsum = min([sum(i) for i in groups]) | ||
max_groupsum = max([sum(i) for i in groups]) | ||
min_groupsum_indices, max_groupsum_indices = [], [] | ||
for i in range(len(groups)): | ||
print("Group: %s, numbers: %s"%(i, groups[i])) | ||
if sum(groups[i]) == min_groupsum: | ||
min_groupsum_indices.append(i) | ||
if sum(groups[i]) == max_groupsum: | ||
max_groupsum_indices.append(i) | ||
print("Min group sum: %s, Max group sum: %s, difference: %s" % (min_groupsum, max_groupsum, max_groupsum-min_groupsum)) | ||
print("Group(s) with min sum: %s" % ",".join([str(groups[i]) for i in min_groupsum_indices])) | ||
print("Group(s) with max sum: %s" % ",".join([str(groups[i]) for i in max_groupsum_indices])) | ||
return groups, max_groupsum-min_groupsum | ||
|
||
if __name__ == "__main__": | ||
partition() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
"""partition: Python number partition algorithm library | ||
""" | ||
|
||
from setuptools import setup | ||
|
||
|
||
version = "0.0.1" | ||
|
||
setup( | ||
include_package_data=True, | ||
name='partition', | ||
version=version, | ||
packages=['partition'], | ||
entry_points={ | ||
'console_scripts': [ | ||
'partition = partition.partition:partition', | ||
] | ||
}, | ||
url='https://github.com/slxiao/partition', | ||
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4', | ||
license='MIT', | ||
author='slxiao', | ||
author_email='[email protected]', | ||
description='Python number partition algorithm library', | ||
classifiers=[ | ||
'Intended Audience :: Developers', | ||
'Natural Language :: English', | ||
'License :: OSI Approved :: BSD License', | ||
'Operating System :: OS Independent', | ||
'Programming Language :: Python', | ||
'Programming Language :: Python :: 2', | ||
'Programming Language :: Python :: 2.7', | ||
'Programming Language :: Python :: 3', | ||
'Programming Language :: Python :: 3.6', | ||
'Programming Language :: Python :: 3.7', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import pytest | ||
|
||
@pytest.fixture | ||
def numbers(): | ||
return [4, 5, 6, 7, 8] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from partition import argument | ||
|
||
|
||
def test_get_parser(mocker): | ||
mocked_parser = mocker.patch("argparse.ArgumentParser") | ||
argument.get_parser() | ||
mocked_parser.assert_called() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from partition import dp | ||
|
||
def test_dp(numbers): | ||
assert dp.dp(numbers) == 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from partition import greedy | ||
|
||
def test_greedy(numbers): | ||
groups = greedy.greedy(numbers) | ||
assert(min([sum(i) for i in groups])) == 13 | ||
assert(max([sum(i) for i in groups])) == 17 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from partition import kk | ||
|
||
def test_kk(numbers): | ||
groups = kk.kk(numbers) | ||
assert(min([sum(i) for i in groups])) == 14 | ||
assert(max([sum(i) for i in groups])) == 16 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from partition import partition | ||
import pytest | ||
|
||
def test_partition_with_version(mocker): | ||
mocked_parser = mocker.patch("partition.argument.get_parser", mocker.Mock(return_value=mocker.Mock())) | ||
mocked_parser.return_value.parse_args.return_value = mocker.Mock(version="0.0.0") | ||
assert partition.partition() == "0.0.0" | ||
mocked_parser.assert_called() | ||
|
||
def test_partition(mocker): | ||
mocked_parser = mocker.patch("partition.argument.get_parser", mocker.Mock(return_value=mocker.Mock())) | ||
mocked_parser.return_value.parse_args.return_value = mocker.Mock(version=None, numbers="1,2,3", grouplen=2, algorithm="greedy") | ||
mocked_greedy = mocker.patch("partition.greedy.greedy", mocker.Mock(return_value=[[1, 2], [3]])) | ||
assert partition.partition()[1] == 0 | ||
mocked_parser.assert_called() | ||
mocked_greedy.assert_called_once_with([1,2,3],2) | ||
|
||
def test_partition_nonexist_algorithm(mocker): | ||
mocked_parser = mocker.patch("partition.argument.get_parser", mocker.Mock(return_value=mocker.Mock())) | ||
mocked_parser.return_value.parse_args.return_value = mocker.Mock(version=None, numbers="1,2,3", algorithm="foo") | ||
with pytest.raises(SystemExit) as pytest_wrapped_e: | ||
partition.partition() | ||
assert pytest_wrapped_e.type == SystemExit | ||
assert pytest_wrapped_e.value.code == "unsupported partition algorithm: foo" |