Skip to content

Commit

Permalink
Test permutations level by level
Browse files Browse the repository at this point in the history
  • Loading branch information
sqrrm committed Apr 28, 2020
1 parent 05106df commit 92cd7ec
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 19 deletions.
52 changes: 37 additions & 15 deletions common/src/main/java/bisq/common/util/PermutationUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
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
Expand Down Expand Up @@ -65,37 +67,57 @@ public static <T, R> List<T> findMatchingPermutation(R targetValue,
list,
new ArrayList<>(),
predicate,
maxIterations);
new AtomicInteger(maxIterations));
}
}

private static <T, R> List<T> findMatchingPermutation(R targetValue,
List<T> list,
List<List<T>> lists,
BiFunction<R, List<T>, Boolean> predicate,
int maxIterations) {
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;
}
}

if (maxIterations == 0) {
return new ArrayList<>();
}

@NonNull
private static <T, R> List<T> checkLevel(R targetValue,
List<T> previousLevel,
BiFunction<R, List<T>, Boolean> predicate,
int level,
int permutationIndex,
AtomicInteger maxIterations) {
if (previousLevel.size() == 1) {
return new ArrayList<>();
}

maxIterations--;
for (int i = 0; i < list.size(); i++) {
List<T> newList = new ArrayList<>(list);
for (int i = permutationIndex; i < previousLevel.size(); i++) {
if (maxIterations.get() <= 0) {
return new ArrayList<>();
}
List<T> newList = new ArrayList<>(previousLevel);
newList.remove(i);

// We want to avoid testing duplicates
if (!lists.contains(newList)) {
if (level == 0) {
maxIterations.decrementAndGet();
// Check all permutations on this level
if (predicate.apply(targetValue, newList)) {
return newList;
} else {
lists.add(newList);
}
} else {
// Test next level
var result = checkLevel(targetValue, newList, predicate, level - 1, i, maxIterations);
if (!result.isEmpty()) {
return result;
}
}
}

List<T> nextList = lists.remove(0);
return findMatchingPermutation(targetValue, nextList, lists, predicate, maxIterations);
return new ArrayList<>();
}

//TODO optimize algorithm so that it starts from all objects and goes down instead starting with from the bottom.
Expand Down
20 changes: 16 additions & 4 deletions common/src/test/java/bisq/common/util/PermutationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@
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 {


Expand Down Expand Up @@ -109,21 +107,35 @@ public void testFindMatchingPermutation() {
int limit = 1048575;
List<String> result;
List<String> list;
String targetValue;
List<String> expected;
BiFunction<String, List<String>, 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()));


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<String, List<String>, 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());
}


Expand Down

0 comments on commit 92cd7ec

Please sign in to comment.