Last active
January 31, 2018 20:20
-
-
Save helospark/0d0cd636a061acfe461e8b065e254288 to your computer and use it in GitHub Desktop.
Java superpermutation finder
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
package com.helospark; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.concurrent.BlockingQueue; | |
import java.util.concurrent.ConcurrentSkipListSet; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.LinkedBlockingQueue; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.atomic.AtomicInteger; | |
class CustomStringBuilder { | |
private char[] string; | |
private int endIndex; | |
public CustomStringBuilder(int length) { | |
string = new char[length]; | |
endIndex = 0; | |
} | |
public CustomStringBuilder(CustomStringBuilder currentString) { | |
this.string = new char[currentString.string.length]; | |
this.endIndex = currentString.endIndex; | |
for (int i = 0; i < currentString.string.length; ++i) { | |
this.string[i] = currentString.string[i]; | |
} | |
} | |
public char charAt(int index) { | |
return string[index]; | |
} | |
public void trimTo(int index) { | |
endIndex = index; | |
} | |
@Override | |
public String toString() { | |
String result = ""; | |
for (int i = 0; i < endIndex; ++i) { | |
result += string[i]; | |
} | |
return result; | |
} | |
public int length() { | |
return endIndex; | |
} | |
public void append(char charAt) { | |
string[endIndex] = charAt; | |
++endIndex; | |
} | |
} | |
class CustomList { | |
private String[] elements; | |
public CustomList(int size) { | |
elements = new String[size]; | |
} | |
public CustomList(List<String> permutations) { | |
elements = new String[permutations.size()]; | |
for (int i = 0; i < permutations.size(); ++i) { | |
elements[i] = permutations.get(i); | |
} | |
} | |
public CustomList(CustomList permutations) { | |
int previousSize = permutations.elements.length; | |
this.elements = new String[previousSize]; | |
for (int i = 0; i < previousSize; ++i) { | |
this.elements[i] = permutations.elements[i]; | |
} | |
} | |
public void swap(int i, int j) { | |
String tmp = elements[i]; | |
elements[i] = elements[j]; | |
elements[j] = tmp; | |
} | |
public String get(int i) { | |
return elements[i]; | |
} | |
} | |
class TaskHolder { | |
private CustomStringBuilder string; | |
private CustomList list; | |
private int listSize; | |
private TaskHolder(Builder builder) { | |
this.string = builder.string; | |
this.list = builder.list; | |
this.listSize = builder.listSize; | |
} | |
public CustomStringBuilder getString() { | |
return string; | |
} | |
public CustomList getList() { | |
return list; | |
} | |
public int getListSize() { | |
return listSize; | |
} | |
public static Builder builder() { | |
return new Builder(); | |
} | |
public static final class Builder { | |
private CustomStringBuilder string; | |
private CustomList list; | |
private int listSize; | |
private Builder() { | |
} | |
public Builder withString(CustomStringBuilder string) { | |
this.string = string; | |
return this; | |
} | |
public Builder withList(CustomList list) { | |
this.list = list; | |
return this; | |
} | |
public Builder withListSize(int listSize) { | |
this.listSize = listSize; | |
return this; | |
} | |
public TaskHolder build() { | |
return new TaskHolder(this); | |
} | |
} | |
} | |
class SuperPermutationTask { | |
public static final int MAX_PRODUCER_DEPTH = 9; | |
private int number; | |
BlockingQueue<TaskHolder> queue; | |
volatile boolean hasMoreElement = true; | |
volatile AtomicInteger maxSize; | |
volatile AtomicInteger numberOfThreadsRunning = new AtomicInteger(0); | |
volatile Set<String> output; | |
private SuperPermutationTask(Builder builder) { | |
this.number = builder.number; | |
this.queue = builder.queue; | |
this.hasMoreElement = builder.hasMoreElement; | |
this.maxSize = builder.maxSize; | |
this.output = builder.output; | |
} | |
public static Builder builder() { | |
return new Builder(); | |
} | |
public static final class Builder { | |
private int number; | |
private BlockingQueue<TaskHolder> queue; | |
private boolean hasMoreElement; | |
private AtomicInteger maxSize; | |
private Set<String> output = Collections.emptySet(); | |
private Builder() { | |
} | |
public Builder withNumber(int number) { | |
this.number = number; | |
return this; | |
} | |
public Builder withQueue(BlockingQueue<TaskHolder> queue) { | |
this.queue = queue; | |
return this; | |
} | |
public Builder withHasMoreElement(boolean hasMoreElement) { | |
this.hasMoreElement = hasMoreElement; | |
return this; | |
} | |
public Builder withMaxSize(AtomicInteger maxSize) { | |
this.maxSize = maxSize; | |
return this; | |
} | |
public Builder withOutput(Set<String> output) { | |
this.output = output; | |
return this; | |
} | |
public SuperPermutationTask build() { | |
return new SuperPermutationTask(this); | |
} | |
} | |
} | |
public class Main { | |
public static void main(String[] args) { | |
for (int i = 4; i < 7; ++i) { | |
String input = createString(i); | |
SuperPermutationTask processResult = process(input); | |
waitUntilTaskFinished(processResult); | |
findShortest(input, processResult.output); | |
} | |
} | |
private static void waitUntilTaskFinished(SuperPermutationTask processResult) { | |
while (processResult.hasMoreElement || processResult.numberOfThreadsRunning.get() > 0) { | |
try { | |
Thread.sleep(1000); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private static void findShortest(String input, Set<String> processResult) { | |
String shortest = ""; | |
for (String a : processResult) { | |
if (shortest.isEmpty() || a.length() < shortest.length()) { | |
shortest = a; | |
} | |
} | |
System.err.println("######### " + input + " " + shortest + " " + shortest.length()); | |
} | |
private static String createString(int end) { | |
String result = ""; | |
for (int i = 1; i <= end; ++i) { | |
result += i; | |
} | |
return result; | |
} | |
static final int NUM_TASKS = Runtime.getRuntime().availableProcessors(); | |
static ExecutorService executorService = Executors.newFixedThreadPool(NUM_TASKS + 1); | |
static SuperPermutationTask process(String chars) { | |
List<String> permutations = new ArrayList<>(); | |
permutation("", chars, permutations); | |
int maxSize = permutations.size() * chars.length(); | |
SuperPermutationTask task = SuperPermutationTask.builder() | |
.withHasMoreElement(true) | |
.withMaxSize(new AtomicInteger(maxSize)) | |
.withQueue(new LinkedBlockingQueue<>(10)) | |
.withOutput(new ConcurrentSkipListSet<>()) | |
.build(); | |
executorService | |
.submit(() -> superPermutationProducer(new CustomList(permutations), permutations.size(), new CustomStringBuilder(maxSize), 0, task)); | |
for (int i = 0; i < NUM_TASKS; ++i) { | |
task.numberOfThreadsRunning.incrementAndGet(); | |
executorService.submit(() -> consumer(task)); | |
} | |
return task; | |
} | |
static void superPermutationProducer(CustomList permutations, int listSize, CustomStringBuilder currentString, int currentDepth, | |
SuperPermutationTask task) { | |
if (currentDepth == task.MAX_PRODUCER_DEPTH || listSize == 0 || currentString.length() >= task.maxSize.get()) { | |
TaskHolder taskHolder = TaskHolder.builder() | |
.withList(new CustomList(permutations)) | |
.withListSize(listSize) | |
.withString(new CustomStringBuilder(currentString)) | |
.build(); | |
try { | |
task.queue.put(taskHolder); | |
} catch (Exception e) { | |
System.out.println(e); | |
} | |
return; | |
} | |
for (int i = 0; i < listSize; ++i) { | |
int originalSize = currentString.length(); | |
CustomStringBuilder modifiedString = mergeCurrentString(currentString, permutations.get(i)); | |
permutations.swap(i, listSize - 1); | |
superPermutationProducer(permutations, listSize - 1, modifiedString, currentDepth + 1, task); | |
modifiedString.trimTo(originalSize); | |
permutations.swap(i, listSize - 1); | |
} | |
if (currentDepth == 0) { | |
task.hasMoreElement = false; | |
} | |
} | |
static void consumer(SuperPermutationTask task) { | |
try { | |
while (task.hasMoreElement) { | |
TaskHolder taskHolder = task.queue.poll(10, TimeUnit.SECONDS); | |
if (taskHolder != null) { | |
System.out.println("Consuming new task"); | |
int currentMaxSize = task.maxSize.get(); | |
int newMaxSize = superPermutationConsumer(taskHolder.getList(), taskHolder.getListSize(), taskHolder.getString(), currentMaxSize, | |
task.output); | |
synchronized (task.maxSize) { | |
int previous = task.maxSize.get(); | |
if (newMaxSize < previous) { | |
task.maxSize.set(newMaxSize); | |
} | |
} | |
System.out.println("Current min: " + task.maxSize.get()); | |
} | |
} | |
} catch (Exception e) { | |
System.out.println(e); | |
} | |
task.numberOfThreadsRunning.decrementAndGet(); | |
} | |
static int superPermutationConsumer(CustomList permutations, int listSize, CustomStringBuilder currentString, int maxSize, Set<String> output) { | |
if (currentString.length() >= maxSize) { | |
return maxSize; | |
} | |
if (listSize == 0) { | |
System.out.println("Found: '" + currentString + "' length: " + currentString.length() + " " + System.currentTimeMillis()); | |
output.add(currentString.toString()); | |
return currentString.length(); | |
} | |
int newMaxSize = maxSize; | |
for (int i = 0; i < listSize; ++i) { | |
int originalSize = currentString.length(); | |
CustomStringBuilder modifiedString = mergeCurrentString(currentString, permutations.get(i)); | |
permutations.swap(i, listSize - 1); | |
int foundSize = superPermutationConsumer(permutations, listSize - 1, modifiedString, newMaxSize, output); | |
if (foundSize < newMaxSize) { | |
newMaxSize = foundSize; | |
} | |
modifiedString.trimTo(originalSize); | |
permutations.swap(i, listSize - 1); | |
} | |
return newMaxSize; | |
} | |
static CustomStringBuilder mergeCurrentString(CustomStringBuilder currentString, String toMergeWith) { | |
int i = Math.min(toMergeWith.length(), currentString.length()); | |
for (; i >= 0; --i) { | |
boolean doesMatch = true; | |
for (int j = 0; j < i; ++j) { | |
char a = currentString.charAt(currentString.length() - i + j); | |
char b = toMergeWith.charAt(j); | |
if (a != b) { | |
doesMatch = false; | |
break; | |
} | |
} | |
if (doesMatch) { | |
break; | |
} | |
} | |
for (int j = i; j < toMergeWith.length(); ++j) { | |
currentString.append(toMergeWith.charAt(j)); | |
} | |
return currentString; | |
} | |
private static void permutation(String prefix, String str, List<String> result) { | |
int n = str.length(); | |
if (n == 0) { | |
result.add(prefix); | |
} else { | |
for (int i = 0; i < n; i++) { | |
permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i + 1, n), result); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment