From 209d9141eb9f33f06881454427dc962f3f18cad0 Mon Sep 17 00:00:00 2001 From: "minhao.zhou" Date: Mon, 7 Feb 2022 23:29:28 +0800 Subject: [PATCH 1/5] Memoized subset-sum approach --- lib/impls/aetherus.ex | 66 +++++++++++++++++++++++++++++++++++++++++++ lib/pp.ex | 5 ++++ 2 files changed, 71 insertions(+) create mode 100644 lib/impls/aetherus.ex diff --git a/lib/impls/aetherus.ex b/lib/impls/aetherus.ex new file mode 100644 index 0000000..9d75861 --- /dev/null +++ b/lib/impls/aetherus.ex @@ -0,0 +1,66 @@ +defmodule PP.Impls.Aetherus do + @spec solve([pos_integer()]) :: { + {partition1_sum :: non_neg_integer(), partition2_sum :: non_neg_integer()}, + {partition1_length :: non_neg_integer(), partition2_length :: non_neg_integer()}, + {partition1 :: [pos_integer()], partition2 :: [pos_integer()]} + } + def solve(nums) do + sum = Enum.sum(nums) + len = length(nums) + sum_target = div(sum, 2) + len_target = div(len, 2) + + {sum_diff, len_diff, partition1} = + Task.async(fn -> + closest_subset_sum(Enum.with_index(nums), sum_target, len_target) + end) + |> Task.await(:infinity) + + partition2 = + nums |> List.myers_difference(partition1) |> Keyword.get_values(:del) |> List.flatten() + + { + {sum_target + sum_diff, sum - sum_target - sum_diff}, + {len_target + len_diff, len - len_target - len_diff}, + {partition1, partition2} + } + end + + @spec closest_subset_sum([pos_integer()], non_neg_integer(), non_neg_integer()) :: + {integer(), integer(), [pos_integer()]} + defp closest_subset_sum([], sum_target, length_target) do + {-sum_target, -length_target, []} + end + + defp closest_subset_sum([{h, i} | t], sum_target, length_target) do + key = {i, sum_target, length_target} + + case Process.get(key) do + solution when solution != nil -> + solution + + nil -> + Process.put(:misses, Process.get(:misses, 0) + 1) + + {sum_diff1, len_diff1, solution1} = + closest_subset_sum(t, sum_target - h, length_target - 1) + + {sum_diff2, len_diff2, solution2} = closest_subset_sum(t, sum_target, length_target) + + cond do + abs(sum_diff1) < abs(sum_diff2) -> + {sum_diff1, len_diff1, [h | solution1]} + + abs(sum_diff1) > abs(sum_diff2) -> + {sum_diff2, len_diff2, solution2} + + abs(len_diff1) <= abs(len_diff2) -> + {sum_diff1, len_diff1, [h | solution1]} + + true -> + {sum_diff2, len_diff2, solution2} + end + |> tap(fn solution -> Process.put(key, solution) end) + end + end +end diff --git a/lib/pp.ex b/lib/pp.ex index 00793ff..ce53900 100644 --- a/lib/pp.ex +++ b/lib/pp.ex @@ -22,4 +22,9 @@ defmodule PP do end end) end + + def impl_aetherus(list) when is_list(list) do + {{sum1, sum2}, _, {part1, part2}} = PP.Impls.Aetherus.solve(list) + {part1, part2, sum1, sum2} + end end From 61f3f180d271e1884e17c90760e3afcd14c263e3 Mon Sep 17 00:00:00 2001 From: "minhao.zhou" Date: Mon, 7 Feb 2022 23:55:22 +0800 Subject: [PATCH 2/5] Remove unused code --- lib/impls/aetherus.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/impls/aetherus.ex b/lib/impls/aetherus.ex index 9d75861..5a68961 100644 --- a/lib/impls/aetherus.ex +++ b/lib/impls/aetherus.ex @@ -40,8 +40,6 @@ defmodule PP.Impls.Aetherus do solution nil -> - Process.put(:misses, Process.get(:misses, 0) + 1) - {sum_diff1, len_diff1, solution1} = closest_subset_sum(t, sum_target - h, length_target - 1) From 52f69e3b9856cdb9a82ec0b2bf99c719d853f10e Mon Sep 17 00:00:00 2001 From: "minhao.zhou" Date: Tue, 8 Feb 2022 00:14:25 +0800 Subject: [PATCH 3/5] Format code and fix typespec --- lib/impls/aetherus.ex | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/impls/aetherus.ex b/lib/impls/aetherus.ex index 5a68961..f511063 100644 --- a/lib/impls/aetherus.ex +++ b/lib/impls/aetherus.ex @@ -17,7 +17,10 @@ defmodule PP.Impls.Aetherus do |> Task.await(:infinity) partition2 = - nums |> List.myers_difference(partition1) |> Keyword.get_values(:del) |> List.flatten() + nums + |> List.myers_difference(partition1) + |> Keyword.get_values(:del) + |> List.flatten() { {sum_target + sum_diff, sum - sum_target - sum_diff}, @@ -26,7 +29,11 @@ defmodule PP.Impls.Aetherus do } end - @spec closest_subset_sum([pos_integer()], non_neg_integer(), non_neg_integer()) :: + @spec closest_subset_sum( + [{item :: pos_integer(), index :: non_neg_integer()}], + non_neg_integer(), + non_neg_integer() + ) :: {integer(), integer(), [pos_integer()]} defp closest_subset_sum([], sum_target, length_target) do {-sum_target, -length_target, []} From 970de4d00110848f82e7bd1c8e92bac16ff6e05d Mon Sep 17 00:00:00 2001 From: "minhao.zhou" Date: Tue, 8 Feb 2022 09:45:49 +0800 Subject: [PATCH 4/5] Optimization --- lib/impls/aetherus.ex | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/impls/aetherus.ex b/lib/impls/aetherus.ex index f511063..a7cf652 100644 --- a/lib/impls/aetherus.ex +++ b/lib/impls/aetherus.ex @@ -5,6 +5,14 @@ defmodule PP.Impls.Aetherus do {partition1 :: [pos_integer()], partition2 :: [pos_integer()]} } def solve(nums) do + + # Reversely sort the nums + # so that subset-sum can converge faster + nums = + nums + |> Enum.sort() + |> Enum.reverse() + sum = Enum.sum(nums) len = length(nums) sum_target = div(sum, 2) @@ -39,6 +47,11 @@ defmodule PP.Impls.Aetherus do {-sum_target, -length_target, []} end + # Truncates the cases + defp closest_subset_sum(_, sum_target, length_target) when sum_target <= 0 do + {-sum_target, -length_target, []} + end + defp closest_subset_sum([{h, i} | t], sum_target, length_target) do key = {i, sum_target, length_target} From b59dbfb37fca9c0908261718c9e0fdec9de26883 Mon Sep 17 00:00:00 2001 From: "minhao.zhou" Date: Tue, 8 Feb 2022 09:56:18 +0800 Subject: [PATCH 5/5] Minor change --- lib/impls/aetherus.ex | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/impls/aetherus.ex b/lib/impls/aetherus.ex index a7cf652..9465e51 100644 --- a/lib/impls/aetherus.ex +++ b/lib/impls/aetherus.ex @@ -8,10 +8,7 @@ defmodule PP.Impls.Aetherus do # Reversely sort the nums # so that subset-sum can converge faster - nums = - nums - |> Enum.sort() - |> Enum.reverse() + nums = Enum.sort(nums, :desc) sum = Enum.sum(nums) len = length(nums)