Skip to content

Instantly share code, notes, and snippets.

@helospark
Last active January 31, 2018 20:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save helospark/0d0cd636a061acfe461e8b065e254288 to your computer and use it in GitHub Desktop.
Save helospark/0d0cd636a061acfe461e8b065e254288 to your computer and use it in GitHub Desktop.
Java superpermutation finder
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