From 96575dcad7c7c61507d50ab3950c3b5014f48ec3 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Wed, 29 Apr 2020 01:00:53 +0200 Subject: [PATCH] Test permutations level by level --- .../bisq/common/util/PermutationUtil.java | 64 +++++++++++++------ .../bisq/common/util/PermutationTest.java | 20 ++++-- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/bisq/common/util/PermutationUtil.java b/common/src/main/java/bisq/common/util/PermutationUtil.java index ac4688fc24..a7c3e98023 100644 --- a/common/src/main/java/bisq/common/util/PermutationUtil.java +++ b/common/src/main/java/bisq/common/util/PermutationUtil.java @@ -21,8 +21,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -65,7 +67,7 @@ public class PermutationUtil { list, new ArrayList<>(), predicate, - maxIterations); + new AtomicInteger(maxIterations)); } } @@ -73,29 +75,49 @@ public class PermutationUtil { List list, List> lists, BiFunction, Boolean> predicate, - int maxIterations) { - - if (maxIterations == 0) { - return new ArrayList<>(); - } - - maxIterations--; - for (int i = 0; i < list.size(); i++) { - List newList = new ArrayList<>(list); - newList.remove(i); - - // We want to avoid testing duplicates - if (!lists.contains(newList)) { - if (predicate.apply(targetValue, newList)) { - return newList; - } else { - lists.add(newList); - } + AtomicInteger maxIterations) { + for (int level = 0; level < list.size(); level++) { + // Test one level at a time + var result = checkLevel(targetValue, list, predicate, level, 0, maxIterations); + if (!result.isEmpty()) { + return result; } } - List nextList = lists.remove(0); - return findMatchingPermutation(targetValue, nextList, lists, predicate, maxIterations); + return new ArrayList<>(); + } + + @NonNull + private static List checkLevel(R targetValue, + List previousLevel, + BiFunction, Boolean> predicate, + int level, + int permutationIndex, + AtomicInteger maxIterations) { + if (previousLevel.size() == 1) { + return new ArrayList<>(); + } + for (int i = permutationIndex; i < previousLevel.size(); i++) { + if (maxIterations.get() <= 0) { + return new ArrayList<>(); + } + List newList = new ArrayList<>(previousLevel); + newList.remove(i); + if (level == 0) { + maxIterations.decrementAndGet(); + // Check all permutations on this level + if (predicate.apply(targetValue, newList)) { + return newList; + } + } else { + // Test next level + var result = checkLevel(targetValue, newList, predicate, level - 1, i, maxIterations); + if (!result.isEmpty()) { + return result; + } + } + } + return new ArrayList<>(); } //TODO optimize algorithm so that it starts from all objects and goes down instead starting with from the bottom. diff --git a/common/src/test/java/bisq/common/util/PermutationTest.java b/common/src/test/java/bisq/common/util/PermutationTest.java index 4a00269990..dc3c65c513 100644 --- a/common/src/test/java/bisq/common/util/PermutationTest.java +++ b/common/src/test/java/bisq/common/util/PermutationTest.java @@ -23,13 +23,11 @@ import java.util.Collections; import java.util.List; import java.util.function.BiFunction; -import lombok.extern.slf4j.Slf4j; import org.junit.Test; import static org.junit.Assert.assertTrue; -@Slf4j public class PermutationTest { @@ -109,13 +107,12 @@ public class PermutationTest { int limit = 1048575; List result; List list; - String targetValue; List expected; BiFunction, Boolean> predicate = (target, variationList) -> variationList.toString().equals(target); list = Arrays.asList(a, b, c, d, e); - expected = Arrays.asList("-"); + expected = Arrays.asList(a); result = PermutationUtil.findMatchingPermutation(expected.toString(), list, predicate, limit); assertTrue(expected.toString().equals(result.toString())); @@ -123,7 +120,22 @@ public class PermutationTest { expected = Arrays.asList(a, c, e); result = PermutationUtil.findMatchingPermutation(expected.toString(), list, predicate, limit); assertTrue(expected.toString().equals(result.toString())); + } + @Test + public void testBreakAtLimit() { + BiFunction, Boolean> predicate = + (target, variationList) -> variationList.toString().equals(target); + var list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"); + var expected = Arrays.asList("b", "g", "m"); + + // Takes around 32508 tries starting from longer strings + var limit = 100000; + var result = PermutationUtil.findMatchingPermutation(expected.toString(), list, predicate, limit); + assertTrue(expected.toString().equals(result.toString())); + limit = 1000; + result = PermutationUtil.findMatchingPermutation(expected.toString(), list, predicate, limit); + assertTrue(result.isEmpty()); }