Compare Lists in ArrayList - java

I have a text file containing the following strings (which are versions of a software):
1_10_2_0_154
3_10_5_2_10
2_10_4_1
3_10_5_1_37
I'm trying to find the most recent version, in this case 3_10_5_2_10 is the version that I'm trying to display using java.
For the moment, here is my code:
BufferedReader br;
String version;
ArrayList<List<Integer>> array = new ArrayList<List<Integer>>();
List<Integer> liste = new ArrayList<Integer>();
try{
br = new BufferedReader(new FileReader(new File(FILEPATH)));
while((version= br.readLine()) != null)
{
liste = Arrays.asList(version.split("_")).stream().
map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList());
array.add(liste);
}
for(int i = 0; i < array.size(); i++)
{
for (List l: array)
{
Object z = l.get(i);
List<Object> listes = new ArrayList<Object>();
listes.add(z);
System.out.println(listes);
}
}
br.close();
System.out.println(array);
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
I made a loop to save strings to ArrayList> like:
[[1,10,2,0,154] , [3,10,5,2,10], [2,10,4,1], [3,10,5,1,37]]
I want to get the elements of each list and compare them to find the most biggest one (most recent one) but I don't know to do that..

I sugguest you a object approach, define a class named Version with compareTo method, then using method sort on Collections class you can simply sort your versions.
Advantages
Clean and Clear code
Data validation
Main:
public class Main {
public static void main(String[] args){
List<Version> versions = Arrays.asList(
Version.create("1_10_2_0_154"),
Version.create("3_10_5_2_10"),
Version.create("2_10_4_1_49"),
Version.create("3_10_5_1_37"));
versions.sort(Version::compareTo);
System.out.println(versions.get(0).toString());
}
}
Version:
public class Version implements Comparable<Version> {
private final int major;
private final int minor;
private final int bug;
private final int release;
private final int build;
public Version(int major, int minor, int bug, int release, int build) {
this.major = major;
this.minor = minor;
this.bug = bug;
this.release = release;
this.build = build;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
public int getBug() {
return bug;
}
public int getRelease() {
return release;
}
public int getBuild() {
return build;
}
#Override
public String toString() {
return "Version{" +
"major=" + major +
", minor=" + minor +
", bug=" + bug +
", release=" + release +
", build=" + build +
'}';
}
public static Version create(String value){
String[] splitRes = value.split("_");
List<Integer> intValues = new ArrayList<>();
for(String v : splitRes){
intValues.add(Integer.parseInt(v));
}
return create(intValues);
}
public static Version create(List<Integer> values){
if(Objects.requireNonNull(values).size() < 5)
throw new IllegalArgumentException();
return new Version(
values.get(0),
values.get(1),
values.get(2),
values.get(3),
values.get(4)
);
}
#Override
public int compareTo(Version that) {
if (this.major > that.major) {
return -1;
} else if (this.major < that.major) {
return 1;
}
if (this.minor > that.minor) {
return -1;
} else if (this.minor < that.minor) {
return 1;
}
if (this.bug > that.bug) {
return -1;
} else if (this.bug < that.bug) {
return 1;
}
if (this.release > that.release) {
return -1;
} else if (this.release < that.release) {
return 1;
}
if (this.build > that.build) {
return -1;
} else if (this.build < that.build) {
return 1;
}
return 0;
}
}
UPDATE 1
As suggested by #Henrik i updated the list sorting with a Java 8 approach.
UPDATE 2
I reversed the compareTo method so now you can simply do plain sort calling sort method on list and passing method reference Version::compareTo
UPDATE 3
A more dynamic solution for Version class:
public class Version implements Comparable<Version> {
private final List<Integer> values;
public Version(List<Integer> values) {
this.values = values;
}
public List<Integer> getValues() {
return values;
}
#Override
public String toString() {
return String.join("_", values
.stream()
.map(Object::toString)
.collect(Collectors.toList()));
}
#Override
public int compareTo(Version that) {
List<Integer> thatValues = that.getValues();
for(int index = 0; index < values.size(); index++){
Integer value = values.get(index);
Integer thatValue = thatValues.get(index);
if (value > thatValue) {
return -1;
} else if (value < thatValue) {
return 1;
}
}
return 0;
}
public static Version create(String value){
String[] splitRes = value.split("_");
List<Integer> intValues = new ArrayList<>();
for(String v : splitRes){
intValues.add(Integer.parseInt(v));
}
return new Version(intValues);
}
}

You can write a Comparator to compare two Lists
Comparator<List<Integer>> comparator = (list1, list2) -> {
Iterator<Integer> iteratorA = list1.iterator();
Iterator<Integer> iteratorB = list2.iterator();
//It iterates through each list looking for an int that is not equal to determine which one precedes the other
while (iteratorA.hasNext() && iteratorB.hasNext()) {
int elementA = iteratorA.next();
int elementB = iteratorB.next();
if (elementA > elementB) {
return 1;
} else if (elementA < elementB) {
return -1;
}
}
//All elements seen so far are equal. Use the list size to decide
return iteratorA.hasNext() ? 1 : iteratorB.hasNext() ? -1 : 0;
};
You can sort it as
Collections.sort(list, comparator);
EDIT: You can refer to David Geirola's answer to convert the version string as a POJO and move the comparator logic inside that. But that is highly tied/coupled to the input string format. My solution works for any List<List<Integer>>.

A simple object oriented approach would be to create object, representing version number, let's call it VersionNumber, which would have a constructor of a factory method that does the parsing of the string. This VersionNumber class should implement interface Comparable and implement method compareTo.
Here is a hint for using Comparable Why should a Java class implement comparable?
Then you can easily write an algorithm to find the max version or google some library that would do it for you.

It is not optimized but should work. You can use both of comparators.
static List<String> versions = Arrays.asList(
"1_10_2_0_154",
"3_10_5_2_10",
"2_10_4_1_49",
"3_10_5_1_37");
static Comparator<List<Integer>> c = (o1,o2) -> {
int length = o1.size()>o2.size()?o2.size():o1.size();
for (int i = 0; i < length; i++) {
int i1 = o1.get(i);
int i2 = o2.get(i);
if (i1 != i2)
return i1 - i2;
}
return 0;
};
static Comparator<List<Integer>> c2 = (o1,o2) -> {
Iterator<Integer> i1=o1.iterator();
Iterator<Integer> i2=o2.iterator();
while (i1.hasNext() && i2.hasNext()){
int i = i1.next()-i2.next();
if (i!=0) return i;
}
return 0;
};
static Optional<List<Integer>> getTheMostRecentVersion(List<String> versions) {
return versions.stream().
map(s -> Arrays.stream(s.split("_")).
map(Integer::parseInt).
collect(Collectors.toList())).max(c2);
}

I think that this text file could be very big and it is better to compare each line on the fly (instead of store all line into collection to sort it after):
public static String getMostRecentVersion(BufferedReader in) throws IOException {
final Comparator<String[]> version = (s1, s2) -> {
int res = 0;
for (int i = 0; i < 5 && res == 0; i++)
res = Integer.compare(Integer.parseInt(s1[i]), Integer.parseInt(s2[i]));
return res;
};
String str;
String resStr = null;
String[] resPparts = null;
while ((str = in.readLine()) != null) {
String[] parts = str.split("_");
if (resStr == null || version.compare(parts, resPparts) > 0) {
resStr = str;
resPparts = parts;
}
}
return resStr;
}

A general ListComparator should help.
static class ListComparator<T extends Comparable<T>> implements Comparator<List<T>> {
#Override
public int compare(List<T> o1, List<T> o2) {
for (int i = 0; i < Math.max(o1.size(), o2.size()); i++) {
int diff =
// Off the end of both - same.
i >= o1.size() && i >= o2.size() ? 0
// Off the end of 1 - the other is greater.
: i >= o1.size() ? -1
: i >= o2.size() ? 1
// Normal diff.
: o1.get(i).compareTo(o2.get(i));
if (diff != 0) {
return diff;
}
}
return 0;
}
}
private static final Comparator<List<Integer>> BY_VERSION = new ListComparator<Integer>().reversed();
public void test(String[] args) {
String[] tests = {
"1_10_2_0_154",
"3_10_5_2_10",
"2_10_4_1_49",
"3_10_5_1_37",
"3_10_5_1_37_0"
};
System.out.println("Before: " + Arrays.toString(tests));
System.out.println("After: " + Arrays.stream(tests)
// Split into parts.
.map(s -> s.split("_"))
// Map String[] to List<Integer>
.map(a -> Arrays.stream(a).map(s -> Integer.valueOf(s)).collect(Collectors.toList()))
// Sort it.
.sorted(BY_VERSION)
// Back to a new list.
.collect(Collectors.toList()));
}

slap your arrays together into a number then just do number comparison.
class Scratch
{
public static void main(String[] args)
{
List<List<Integer>> arr = new ArrayList<>();
arr.add(fromArray(new Integer[]{1,10,2,0,154}));
arr.add(fromArray(new Integer[]{3,10,5,2,10}));
arr.add(fromArray(new Integer[]{2,10,4,1,49}));
arr.add(fromArray(new Integer[]{3,10,5,1,37}));
Integer[] maxLengths = {0,0,0,0,0};
for (List<Integer> v : arr)
{
for(int idx = 0; idx < v.size(); idx++)
{
Integer n = v.get(idx);
int curMaxLen = maxLengths[idx];
maxLengths[idx] = Math.max(n.toString().length(), curMaxLen);
}
}
Long largest = arr.stream().map(v -> {
StringBuilder result = new StringBuilder();
for(int idx = 0; idx < v.size(); idx++)
{
Integer n = v.get(idx);
int maxLen = maxLengths[idx];
result.append(String.format("%-" + maxLen + 's', n).replace(' ', '0'));
}
return result.toString();
}).map(Long::valueOf).max(Comparator.naturalOrder()).get();
System.out.println(largest);
}
public static List<Integer> fromArray(Integer[] array)
{
List<Integer> list = new ArrayList<>();
Collections.addAll(list, array);
return list;
}
}

Related

n-ary Cartesian product inRxJava

Now I hold an Observable<Observable<Integer>, how can I transfer it into Observable<int[]> that contains the n-ary Cartesian product?
For example:
Observable<Observable<Integer> ob = Observable.just(
Observable.just(0,1),
Observable.just(2,3),
Observable.just(4,5)
);
ob...... -> (0,2,4), (0,3,4), (0,2,5), (0,3,5), (1,2,4), (1,3,4), (1,2,5), (1,3,5)
First of all, you need a fixed number of input Observables. Second, there is no need for blocking but there is likely the need for caching because the 2nd, 3rd etc Observables need to be consumed multiple times.
import java.util.*;
import io.reactivex.Observable;
public class Cartesian {
static Observable<int[]> cartesian(Observable<Observable<Integer>> sources) {
return sources.toList().flatMapObservable(list -> cartesian(list));
}
static Observable<int[]> cartesian(List<Observable<Integer>> sources) {
if (sources.size() == 0) {
return Observable.<int[]>empty();
}
Observable<int[]> main = sources.get(0).map(v -> new int[] { v });
for (int i = 1; i < sources.size(); i++) {
int j = i;
Observable<Integer> o = sources.get(i).cache();
main = main.flatMap(v -> {
return o.map(w -> {
int[] arr = Arrays.copyOf(v, j + 1);
arr[j] = w;
return arr;
});
});
}
return main;
}
public static void main(String[] args) {
cartesian(Observable.just(
Observable.just(0, 1),
Observable.just(2, 3),
Observable.just(4, 5)
))
.subscribe(v -> System.out.println(Arrays.toString(v)));
}
}
Creating Cartesian product in an asynchronous way is hard or in some sense is impossible. And if blocking is OK, you can do something like this
public class Main
{
static class ProductIterator<T> implements Iterator<T[]>
{
private final List<List<T>> componentsList;
private final Class<T> componentClass;
private final int[] indices;
private boolean hasNext;
public ProductIterator(List<List<T>> componentsList, Class<T> componentClass)
{
this.componentsList = componentsList;
this.componentClass = componentClass;
this.indices = new int[componentsList.size()];
this.hasNext = this.indices[componentsList.size() - 1] < componentsList.get(componentsList.size() - 1).size();
}
#Override
public boolean hasNext()
{
return hasNext;
}
#Override
public T[] next()
{
T[] res = (T[]) Array.newInstance(componentClass, componentsList.size());
for (int i = 0; i < componentsList.size(); i++)
{
res[i] = componentsList.get(i).get(indices[i]);
}
// move next
indices[0]++;
for (int i = 0; i < componentsList.size() - 1; i++)
{
if (indices[i] == componentsList.get(i).size())
{
indices[i] = 0;
indices[i + 1]++;
}
}
hasNext = indices[componentsList.size() - 1] < componentsList.get(componentsList.size() - 1).size();
return res;
}
}
public static <T> Observable<T[]> product(Observable<Observable<T>> components, Class<T> componentClass)
{
return Observable.fromIterable(new Iterable<T[]>()
{
#Override
public Iterator<T[]> iterator()
{
// postpone blocking up until iterator is requested
// and by this point we can't postpone anymore
Single<List<List<T>>> componentsList = components.map(o -> o.toList().blockingGet()).toList();
return new ProductIterator<T>(componentsList.blockingGet(), componentClass);
}
});
}
public static void main(String[] args) throws Exception
{
Observable<Observable<Integer>> ob = Observable.just(
Observable.just(0, 1),
Observable.just(2, 3),
Observable.just(4, 5)
);
Observable<Integer[]> product = product(ob, Integer.class);
product.forEach(a -> System.out.println(Arrays.toString(a)));
}
}
It is possible to improve this code to avoid blocking but you still will have to cache all results from all Observables and code will be much more complicated. Most probably is blocking is not acceptable for you, trying to get Cartesian product is bad idea anyway.
Well, I can resolve it myself. But is there any more elegant way?
(The toArray method convert an Observable<T> to T[])
Observable<int[]> toObservableArray(Observable<Observable<Integer>> obs) {
List<int[]> list = obs.map(ob -> toArray(ob)).toList().toBlocking().last();
return Observable.create(new SyncOnSubscribe<int[], int[]>() {
#Override
protected int[] generateState() {
int[] array = new int[list.size()];
Arrays.fill(array, 0);
return array;
}
#Override
protected int[] next(int[] state, Observer<? super int[]> observer) {
int[] next = new int[list.size()];
for (int i = 0; i < next.length; i++) {
next[i] = list.get(i)[state[i]];
}
observer.onNext(next);
state[state.length - 1]++;
for (int i = state.length - 1; i >= 0; i--) {
int delta = list.get(i).length - state[i];
if (delta > 0) {
break;
} else if (delta == 0) {
state[i] = 0;
if (i == 0) {
observer.onCompleted();
break;
}
state[i - 1]++;
}
}
return state;
}
});
}

[Hackerrank][Performance Improvement] Similar Destinations

I am currently solving a challenge that I found on Hackerrank and am in need of some assistance in the code optimization/performance department. I've managed to get my code working and returning the right results but it is failing at the final test case with a timeout error. The input is quite large so, that explains why the code is taking longer that expected.
Problem statement: Similar Destinations
I've attempted to think of different ways of pruning my (intermediate) result set but could not come up with something that I did not already have. I believe that the find function could use a bit more tweaking. I've tried my best to reduce the number of paths that the recursive function has to take but ultimately, it has to look at every destination in order to come up with the right results. However, I did terminate a recursive path if the number of tags in common between destinations were below the min limit. Is there anything else that I could do here?
My code is as follows:-
static class Destination {
String dest;
List<String> tags;
public Destination(String dest, List<String> tags) {
this.dest = dest;
this.tags = tags;
}
#Override
public String toString() {
return dest;
}
}
static List<Destination> allDest = new ArrayList<Destination>();
static int min;
static Set<String> keysTracker = new HashSet<String>();
static Set<String> tagsTracker = new HashSet<String>();
static Map<String, List<String>> keysAndTags = new HashMap<String, List<String>>();
static void find(List<String> commonKey, List<String> commonTags, int index) {
if (index >= allDest.size())
return;
if (commonTags.size() < min)
return;
if (tagsTracker.contains(commonTags.toString()) || keysTracker.contains(commonKey.toString())) {
return;
}
String dest = allDest.get(index).dest;
commonKey.add(dest);
for (int i = index + 1; i < allDest.size(); ++i) {
List<String> tempKeys = new ArrayList<String>(commonKey);
List<String> tags = allDest.get(i).tags;
List<String> tempTags = new ArrayList<String>(commonTags);
tempTags.retainAll(tags);
find(tempKeys, tempTags, i);
if (tempTags.size() >= min) {
if (!tagsTracker.contains(tempTags.toString())
&& !keysTracker.contains(tempKeys.toString())) {
tagsTracker.add(tempTags.toString());
keysTracker.add(tempKeys.toString());
StringBuilder sb = new StringBuilder();
for (int j = 0; j < tempKeys.size(); ++j) {
sb.append(tempKeys.get(j));
if (j + 1 < tempKeys.size())
sb.append(",");
}
keysAndTags.put(sb.toString(), tempTags);
}
}
}
}
public static void main(String[] args) {
init();
sort();
calculate();
answer();
}
static void init() {
Scanner s = new Scanner(System.in);
min = s.nextInt();
s.nextLine();
String line;
while (s.hasNextLine()) {
line = s.nextLine();
if (line.isEmpty())
break;
String[] tokens = line.split(":");
String dest = tokens[0];
tokens = tokens[1].split(",");
List<String> tags = new ArrayList<String>();
for (int j = 0; j < tokens.length; ++j)
tags.add(tokens[j]);
Collections.sort(tags);
Destination d = new Destination(dest, tags);
allDest.add(d);
}
s.close();
}
static void sort() {
Collections.sort(allDest, new Comparator<Destination>() {
#Override
public int compare(Destination d1, Destination d2) {
return d1.dest.compareTo(d2.dest);
}
});
}
static void calculate() {
for (int i = 0; i < allDest.size() - 1; ++i) {
find(new ArrayList<String>(), new ArrayList<String>(allDest.get(i).tags), i);
}
}
static void answer() {
List<Map.Entry<String, List<String>>> mapInListForm = sortAnswer();
for (Map.Entry<String, List<String>> entry : mapInListForm) {
System.out.print(entry.getKey() + ":");
for (int i = 0; i < entry.getValue().size(); ++i) {
System.out.print(entry.getValue().get(i));
if (i + 1 < entry.getValue().size())
System.out.print(",");
}
System.out.println();
}
}
static List<Map.Entry<String, List<String>>> sortAnswer() {
List<Map.Entry<String, List<String>>> mapInListForm =
new LinkedList<Map.Entry<String, List<String>>>(keysAndTags.entrySet());
Collections.sort(mapInListForm, new Comparator<Map.Entry<String, List<String>>>() {
public int compare(Map.Entry<String, List<String>> e1, Map.Entry<String, List<String>> e2) {
if (e1.getValue().size() > e2.getValue().size()) {
return -1;
} else if (e1.getValue().size() < e2.getValue().size()) {
return 1;
}
return e1.getKey().compareTo(e2.getKey());
}
});
return mapInListForm;
}
Any help is greatly appreciated. Thanks!
I've managed to solve the problem after a bit of selective profiling. It would seem that my initial hunch was right. The problem had less to do with the algorithm and more towards the data structures that I was using! The culprit was in the find method. Specifically, when calling the retainAll method on two lists. I had forgotten the that it would take O(n^2) time to iterate through two lists. That was why it was slow. I then changed list into a HashSet instead. As most of us know, a HashSet has an O(1) time complexity when it comes to accessing its values. The retainAll method stayed but instead of finding the intersection between two lists, we now find the intersection between two sets instead! That managed to shave off a couple of seconds off of the total elapsed runtime and all the tests passed. :)
The find method now looks like this:-
static void find(List<String> commonKey, List<String> commonTags, int index) {
if (index >= allDest.size())
return;
if (commonTags.size() < min)
return;
if (tagsTracker.contains(commonTags.toString()) || keysTracker.contains(commonKey.toString())) {
return;
}
String dest = allDest.get(index).dest;
commonKey.add(dest);
for (int i = index + 1; i < allDest.size(); ++i) {
List<String> tempKeys = new ArrayList<String>(commonKey);
List<String> tags = allDest.get(i).tags;
Set<String> tempTagsSet1 = new HashSet<String>(commonTags);
Set<String> tempTagsSet2 = new HashSet<String>(tags);
tempTagsSet1.retainAll(tempTagsSet2);
List<String> tempTags = new ArrayList<String>(tempTagsSet1);
if (tempTags.size() >= min)
Collections.sort(tempTags);
find(tempKeys, tempTags, i);
if (tempTags.size() >= min) {
if (!tagsTracker.contains(tempTags.toString())
&& !keysTracker.contains(tempKeys.toString())) {
tagsTracker.add(tempTags.toString());
keysTracker.add(tempKeys.toString());
StringBuilder sb = new StringBuilder();
for (int j = 0; j < tempKeys.size(); ++j) {
sb.append(tempKeys.get(j));
if (j + 1 < tempKeys.size())
sb.append(",");
}
keysAndTags.put(sb.toString(), tempTags);
}
}
}
}

How to design a test interface that times only some part of the code?

I want to time how long my code takes to perform on average over many test runs. In each test run, doWork() performs the work that I want to time. But I also want to checkWork() in each test run without it counting towards the time. I'm going to have many similar Exercise# classes, so I'd like to abstract the testing via a TestInterface. Is my current way a reasonable approach? Or is there a better design pattern / standard approach? Thanks in advance.
#FunctionalInterface
public interface TestInterface {
void test(final int NUM_TESTS);
}
public class TimeTests {
public static void test(TestInterface ti, final int NUM_TESTS, String testName) {
DecimalFormat df = new DecimalFormat("#.####");
long start = System.nanoTime();
ti.test(NUM_TESTS);
System.out.println("DEBUG: " + testName + " took "
+ df.format((System.nanoTime() - start) * 1.0 / NUM_TESTS)
+ " nanoseconds on average for " + NUM_TESTS + " tests");
}
}
public class Exercise1 {
private static final int NUM_TESTS = (int) Math.pow(10, 6);
private static void mainWork(List<Integer> A) {
// do stuff and time it
}
private static void checkWork(List<Integer> A) {
// do stuff but don't count it towards the time
}
public static void main(String[] args) {
TimeTests.test((NUM_TESTS_LOCAL) -> {
for (int i = 0; i < NUM_TESTS_LOCAL; ++i) {
List<Integer> A = new ArrayList<>();
// add random elements to A
mainWork(A);
checkWork(A);
}
}, NUM_TESTS, "Exercise1");
}
}
Okay, I think I managed to put together a decent framework (is this the right word?) for this task. If anybody could chime in to let me know if my approach is any good, I'd really appreciate it.
While my code seems to work fine for my use cases so far, I have a few questions:
In the interface definition of public interface CloneableTestInput<T extends CloneableTestInput<T>>, how is the type template <T extends CloneableTestInput<T> not a circular definition? I'm not sure I fully understand what that type template is saying.
Is there a way to make a generic CloneableList class that implements CloneableTestInput<List>? Currently, I need to make a separate implementation for each Collection type (e.g. ArrayList, LinkedList, ...). Similarly, is it possible to make a generic CloneableSet class that implements CloneableTestInput<Set>?
Thanks in advance :)
Testing Framework
Part I - An interface for test inputs
This allows TimeTests.java to work for generic input types.
public interface CloneableTestInput<T extends CloneableTestInput<T>> extends Cloneable {
T clone();
}
public class CloneableString implements CloneableTestInput<CloneableString> {
public String data;
public CloneableString() {}
public CloneableString(String input) { data = input; }
public CloneableString clone() { return new CloneableString(String.valueOf(data)); }
}
public class CloneableArrayList extends ArrayList implements CloneableTestInput<CloneableArrayList> {
public CloneableArrayList(ArrayList input) {
this.addAll(input);
}
#Override
public CloneableArrayList clone() {
return new CloneableArrayList(this);
}
}
Part II - An interface for timing tests
#FunctionalInterface
public interface TimeTestsInterface<outputType> {
void test(Callable<CloneableTestInput> formInput
, Function<CloneableTestInput, outputType> runAlgorithm
, Function<CloneableTestInput, outputType> getKnownOutput
, BiFunction<outputType, outputType, Boolean> checkResults
, final int NUM_TESTS, String testName);
}
public class TimeTests<outputType> implements TimeTestsInterface<outputType> {
public void test(Callable<CloneableTestInput> formInput
, Function<CloneableTestInput, outputType> runAlgorithm
, Function<CloneableTestInput, outputType> getKnownOutput
, BiFunction<outputType, outputType, Boolean> checkResults
, final int NUM_TESTS, String testName) {
try {
DecimalFormat df = new DecimalFormat("#.####");
long total = 0, start;
for (int i=0; i < NUM_TESTS; ++i) {
CloneableTestInput input = formInput.call();
CloneableTestInput orig_input = input.clone();
start = System.nanoTime();
outputType algorithmResult = runAlgorithm.apply(input);
total += System.nanoTime() - start;
outputType expectedResult = getKnownOutput.apply(orig_input);
assert(checkResults.apply(algorithmResult, expectedResult));
}
System.out.println("DEBUG: " + testName + " took "
+ df.format(total * 1.0 / NUM_TESTS)
+ " nanoseconds on average for " + NUM_TESTS + " tests");
} catch (Exception|AssertionError e) {
System.out.println(e.toString() + " - " + e.getMessage() + " - ");
e.printStackTrace();
}
}
}
Example Usages
Increment a BigInteger (uses CloneableArrayList)
/**
* Problem 6.2 from EPI
* Given an array A of digits encodiing a decimal number D,
* with MSD at A[0]. Update A to hold D + 1.
*/
public class PlusOne {
private static final int NUM_TESTS = (int) Math.pow(10, 5);
private static final int ARR_LENGTH = (int) Math.pow(10, 2);
private static ArrayList<Integer> plusOne(ArrayList<Integer> A) {
int n = A.size() - 1;
A.set(n, A.get(n) + 1);
for (int i = n; i > 0 && A.get(i) == 10; --i) {
A.set(i, 0);
A.set(i-1, A.get(i-1) + 1);
}
if (A.get(0) == 10) {
// Need additional digit up front as MSD
A.set(0,0);
A.add(0,1);
}
return A;
}
private static ArrayList<Integer> randArray(int len) {
ArrayList<Integer> A = new ArrayList<>();
if (len == 0) return A;
Random rgen = new Random();
A.add(rgen.nextInt(9) + 1);
--len;
while (len != 0) {
A.add(rgen.nextInt(10));
--len;
}
return A;
}
public static void main(String[] args) {
Callable<CloneableTestInput> formInput = () -> new CloneableArrayList(randArray(ARR_LENGTH));
Function<CloneableTestInput, ArrayList<Integer>> runAlgorithm =
(input) -> plusOne((ArrayList<Integer>) input);
Function<CloneableTestInput, ArrayList<Integer>> getKnownOutput =
(orig_input) -> {
BigInteger B = new BigInteger(Joiner.on("").join((ArrayList<Integer>) orig_input));
B = B.add(BigInteger.valueOf(1));
ArrayList<Integer> expectedOutput = new ArrayList<>();
while (B.compareTo(BigInteger.valueOf(0)) > 0) {
expectedOutput.add(0, B.mod(BigInteger.valueOf(10)).intValue());
B = B.divide(BigInteger.valueOf(10));
}
return expectedOutput;
};
BiFunction<ArrayList<Integer>, ArrayList<Integer>, Boolean> checkResults = List::equals;
TimeTests<ArrayList<Integer>> algTimer = new TimeTests<>();
algTimer.test(formInput, runAlgorithm, getKnownOutput, checkResults, NUM_TESTS, "PlusOne");
}
}
Can String be rearranged as a palindrome? (uses CloneableString)
public class CanStringBePalindrome {
private static final int INPUT_STRING_LENGTH = (int) Math.pow(10, 2);
private static final int NUM_TESTS = (int) Math.pow(10, 6);
private static boolean canFormPalindromeHash(final String s) {
Map<Character, Integer> charFreqs = new HashMap<>();
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (!charFreqs.containsKey(c))
charFreqs.put(c, 1);
else
charFreqs.put(c, charFreqs.get(c) + 1);
}
int oddFreqCount = 0;
for (Map.Entry<Character, Integer> entry : charFreqs.entrySet()) {
if ((entry.getValue() % 2) != 0 && (++oddFreqCount > 1))
return false;
}
return true;
}
private static boolean canFormPalindromeSorting(final String s) {
// TODO : find faster/simpler way of getting frequency counts
char[] a = s.toCharArray();
Arrays.sort(a);
int oddFreqCount = 0;
int numCurrChar =1;
for (int i = 1; i < a.length && oddFreqCount <= 1; ++i) {
if(a[i] != a[i-1]) {
if ((numCurrChar & 1) != 0)
++oddFreqCount;
numCurrChar = 1;
} else
++numCurrChar;
}
if ((numCurrChar & 1) != 0)
++oddFreqCount;
return oddFreqCount <= 1;
}
private static String randString(int len) {
StringBuilder sb = new StringBuilder();
Random rgen = new Random();
while (len-- > 0)
sb.append((char)(rgen.nextInt(26) + 'A'));
return sb.toString();
}
public static void main(String[] args) {
Callable<CloneableTestInput> formInput = () -> new CloneableString(randString(INPUT_STRING_LENGTH));
Function<CloneableTestInput, Boolean > runAlgorithm =
(input) -> canFormPalindromeHash(((CloneableString)input).data);
Function<CloneableTestInput, Boolean> getKnownOutput =
(orig_input) -> canFormPalindromeSorting(((CloneableString)orig_input).data);
BiFunction<Boolean, Boolean, Boolean> checkResults = Boolean::equals;
TimeTests<Boolean> algTimer = new TimeTests<>();
algTimer.test(formInput, runAlgorithm, getKnownOutput, checkResults
, NUM_TESTS, "CanStringBePalindrome");
}
}

Can't locate the problems in this simple Genetic Algorithm program

I have written a simple genetic algorithm program in java. What it is does is maximize the decimal value represented by the bits in the chromosome. Somehow mutation is not working as expected, e.g. causing two genes to mutate when just one is to change. The print statements I have included there show which to mutate, but in addition to that some more chromosomes get mutated. I can't figure out what the problem is :-(
Here are my java classes.
Gene.java
public class Gene {
private int value;
public Gene() {
value = Math.random() < 0.5 ? 0 : 1;
}
public Gene(int value) {
if (value != 0 && value != 1) {
throw new IllegalArgumentException("value must be either 0 or 1");
}
else {
this.value = value;
}
}
public void mutate() {
value = 1 - value;
}
public int value() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
}
Chromosome.java
import java.util.ArrayList;
import java.util.List;
public class Chromosome implements Comparable {
private ArrayList<Gene> genes;
private final int chromosomeLength;
public Chromosome(int length) {
this.genes = new ArrayList<>();
this.chromosomeLength = length > 0 ? length : 16;
for (int i = 0; i < chromosomeLength; i++) {
this.genes.add(i, new Gene());
}
}
public List<Gene> getAllele(int fromIndex, int toIndex) {
return new ArrayList<>(genes.subList(fromIndex, toIndex));
}
public void setAllele(int fromIndex, List<Gene> allele) {
int lastIndex = fromIndex + allele.size();
if (lastIndex > chromosomeLength) {
throw new IndexOutOfBoundsException("the allele exceeds beyond the size of the chromosome");
}
for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
genes.set(i, allele.get(j));
}
}
public int getChromosomeLength() {
return chromosomeLength;
}
public void setGeneAt(int index, Gene gene) {
genes.set(index, gene);
}
public Gene getGeneAt(int index) {
return genes.get(index);
}
public int value() {
return Integer.parseInt(this.toString(), 2);
}
#Override
public String toString() {
StringBuilder chromosome = new StringBuilder("");
genes.stream().forEach((Gene g) -> chromosome.append(g));
return chromosome.toString();
}
#Override
public int compareTo(Object anotherChromosome) {
Chromosome c = (Chromosome) anotherChromosome;
return this.value() - c.value();
}
}
GenePool.java
import java.util.ArrayList;
import java.util.Arrays;
public class GenePool {
private final ArrayList<Chromosome> genePool;
private final int genePoolSize;
private final int chromosomeLength;
private final double crossOverRate;
private final double mutationRate;
private int[] crossPoints;
public GenePool(int numOfChromosome, int chromosomeLength, double crossOverRate, double mutationRate) {
this.genePoolSize = numOfChromosome;
this.chromosomeLength = chromosomeLength > 0 ? chromosomeLength : 16;
this.crossOverRate = crossOverRate;
this.mutationRate = mutationRate;
crossPoints = new int[1];
crossPoints[0] = this.chromosomeLength / 2;
genePool = new ArrayList<>();
for (int i = 0; i < numOfChromosome; i++) {
genePool.add(new Chromosome(chromosomeLength));
}
}
public int getGenePoolSize() {
return genePoolSize;
}
public Chromosome getChromosomeAt(int index) {
return genePool.get(index);
}
public void setChromosomeAt(int index, Chromosome c) {
genePool.set(index, c);
}
public int getChromosomeLength() {
return chromosomeLength;
}
public Chromosome[] crossOver(Chromosome c1, Chromosome c2) {
Chromosome[] offsprings = new Chromosome[2];
offsprings[0] = new Chromosome(c1.getChromosomeLength());
offsprings[1] = new Chromosome(c1.getChromosomeLength());
Chromosome[] parentChromosomes = {c1, c2};
int selector = 0;
for (int i = 0, start = 0; i <= crossPoints.length; i++) {
int crossPoint = i == crossPoints.length ? c1.getChromosomeLength() : crossPoints[i];
offsprings[0].setAllele(start, parentChromosomes[selector].getAllele(start, crossPoint));
offsprings[1].setAllele(start, parentChromosomes[1 - selector].getAllele(start, crossPoint));
selector = 1 - selector;
start = crossPoint;
}
return offsprings;
}
public void mutateGenePool() {
int totalGeneCount = genePoolSize * chromosomeLength;
System.out.println("Mutating genes:");
for (int i = 0; i < totalGeneCount; i++) {
double prob = Math.random();
if (prob < mutationRate) {
System.out.printf("Chromosome#: %d\tGene#: %d\n", i / chromosomeLength, i % chromosomeLength);
genePool.get(i / chromosomeLength).getGeneAt(i % chromosomeLength).mutate();
}
}
System.out.println("");
}
public int getLeastFitIndex() {
int index = 0;
int min = genePool.get(index).value();
int currentValue;
for (int i = 1; i < genePoolSize; i++) {
currentValue = genePool.get(i).value();
if (currentValue < min) {
index = i;
min = currentValue;
}
}
return index;
}
public void saveFittest(ArrayList<Chromosome> offsprings) {
// sort in ascending order
offsprings.sort(null);
offsprings.stream().forEach((offspring) -> {
int leastFitIndex = getLeastFitIndex();
if (offspring.value() > genePool.get(leastFitIndex).value()) {
genePool.set(leastFitIndex, offspring);
}
});
}
public void evolve(int noOfGeneration) {
for (int generation = 1; generation <= noOfGeneration; generation++) {
System.out.println("Generation :" + generation);
ArrayList<Integer> selection = new ArrayList<>();
for (int i = 0; i < genePoolSize; i++) {
if (Math.random() <= crossOverRate) {
selection.add(i);
}
}
if (selection.size() % 2 == 1) {
selection.remove(selection.size() - 1);
}
ArrayList<Chromosome> offsprings = new ArrayList<>();
for (int i = 0; i < selection.size(); i += 2) {
int index1 = selection.get(i);
int index2 = selection.get(i + 1);
offsprings.addAll(Arrays.asList(crossOver(genePool.get(index1), genePool.get(index2))));
}
System.out.println("Before saving the offsprings");
displayChromosomes(genePool, "GenePool");
displayChromosomes(offsprings, "Offsprings");
saveFittest(offsprings);
System.out.println("Before mutation:");
displayChromosomes(genePool, "GenePool");
mutateGenePool();
System.out.println("After mutation:");
displayChromosomes(genePool, "GenePool");
System.out.println("\n\n");
}
}
public void displayChromosomes(ArrayList<Chromosome> geneList, String name) {
System.out.println(name);
if (geneList.isEmpty()) {
System.out.println("Empty list");
}
geneList.stream().forEach((c) -> {
System.out.println(c + " -> " + c.value());
});
System.out.println("");
}
}
GADemo.java
public class GADemo {
public static void main(String[] args) {
GenePool gp = new GenePool(6, 8, 0.25, 0.01);
gp.evolve(10);
}
}
After evolving for a number of generations, the chromosomes all tend to become exactly the same, or very similar. And the problem is that that value is not the maximum for that many bits, and sometimes even a small value. For example, for 8 bits the values should (tend to) approach 255, but this doesn't do so in my code. Someone please provide a hint where/how to look for and solve the problem.
Focus on these lines and imagine the references. These are from setAllele()
for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
genes.set(i, allele.get(j));
}
You are basically copying the reference from one onto the other. They are the same Gene so whatever mutation you do on those genes, will also affect even other Chromosomes.
You must produce a deep copy here.
Initially each chromosome has an own list of genes. But when you do the crossover operation you set gene objects from one chromosome into the gene list of other chromosome.
When you evolve the system, the number of shared genes will rise and therefore ultimately all chromosomes will share the same genes. No matter how you mutate a gene the chromosomes are not affected.
EDIT:
As Incognito also answered the setAllele method seems to be the culprit where gene sharing starts. You may want to introduce a method in the gene class where you can set its value given another gene.

Combination of N ordered element

I have a set of K element and i need to create a combination of N ordered element.
For examle if K=1 and i have {X1, emptyset} and n = 2 then i have an ordered pair i need to make this:
Example1:
({},{})
({X1},{}), ({},{X1})
({X1},{X1})
Note that I need to get the element in this order: first the element with 0 node as the sum of both pairs, second the element with 1, ecc
My idea is to make the set of parts of the intial set, adding an element at time, but I'm losing my mind. Any suggestions? I need to do this in java.
EDIT 1:
In other words I need to create an Hasse diagram:
http://en.wikipedia.org/wiki/Hasse_diagram
where every node is an element of the set of parts and the partial-ordering function is the inclusion of on all the subset like this:
Example2:
ni = (S1i,S2i) C nj = (S1j,S2j) only if S1i C S1j AND S21 C s2j
EDIT2: #RONALD:
If I have K=2 for a set S = {1, 2} and n =2, i need this output:
level0: ({}, {})
level1: ({1}, {}); ({2}, {}); ({}, {1}); ({}, {2})
level2: ({1,2}, {}); ({1}, {1}); ({1}, {2}); ({2}, {1}); ({2}, {2}); ({}, {1,2});
[..]
the order is important between levels, for example:
If at level1 i have
({1}, {}); ({2}, {}); ({}, {1}); ({}, {2})
OR
({}, {2}); ({}, {1}); ({2}, {}); ({1}, {});
is the same thing. But it's importat that at level 2 i have all superset of level2 and a superset is explained in example 2
EDIT3:
If my set is S= {x,y,z} and i have only one set per node the result (starting from the bottom) is this:
http://upload.wikimedia.org/wikipedia/commons/e/ea/Hasse_diagram_of_powerset_of_3.svg
If I have S={1,2} and two set per nod the result is this (thanks Ronald for the diagram) :
http://www.independit.de/Downloads/hasse.pdf
EDIT4:
Because is a super-exponential problem my idea is: I compute one level at time (in ordered mode!) and with some rule i prune a node and all his superset. Another stop rule may be to stop at a certain level. For this rule it is essential to calculate combinations directly in an orderly manner and not to calculate all and then reorder.
EDIT5:
The Marco13's code work fine, i have make some modify for:
Use function PowerSet because it's helpfull for make all combination of only K element of a set S (I only need to get the first tot element of powerset for do this).
Now the algorithm do all but i need to speed up it. Is there any way to parallelize the computation? such a way to use Map Reduce (Apache hadoop implementation) paradigm?
package utilis;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class HasseDiagramTest4
{
public static void main(String[] args)
{
int numberOfSetsPerNode = 3;
List<Integer> set = Arrays.asList(1, 2, 3, 4, 5,6);
List<Set<Integer>> powerSet = computePowerSet(set);
powerSet = KPowerSet(powerSet, 3);
List<List<Set<Integer>>> prunedNodes =
new ArrayList<List<Set<Integer>>>();
List<Set<Integer>> prunedNode = new ArrayList<Set<Integer>>();
HashSet<Integer> s = new HashSet<Integer>();
HashSet<Integer> s_vuoto = new HashSet<Integer>();
s.add(1);
s.add(2);
prunedNode.add(s);
prunedNode.add(s_vuoto);
prunedNode.add(s);
prunedNodes.add(prunedNode);
compute(ordina(powerSet), numberOfSetsPerNode, prunedNodes);
}
private static <T> HashMap<Integer, List<Set<T>>> ordina(List<Set<T>> powerSet) {
HashMap<Integer, List<Set<T>>> hs = new HashMap<Integer, List<Set<T>>>();
for(Set<T> l: powerSet)
{
List<Set<T>> lput = new ArrayList<Set<T>>();
if(hs.containsKey(l.size()))
{
lput = hs.get(l.size());
lput.add(l);
hs.put(l.size(), lput);
}
else
{
lput.add(l);
hs.put(l.size(), lput);
}
}
return hs;
}
private static <T> List<Set<T>> KPowerSet(List<Set<T>> powerSet, int k)
{
List<Set<T>> result = new ArrayList<Set<T>>();
for(Set<T>s:powerSet)
{
if(s.size() <= k)
{
result.add(s);
}
}
return result;
}
private static <T> List<Set<T>> computePowerSet(List<T> set)
{
List<Set<T>> result = new ArrayList<Set<T>>();
int numElements = 1 << set.size();
for (int j=0; j<numElements; j++)
{
Set<T> element = new HashSet<T>();
for (int i = 0; i < set.size(); i++)
{
long b = 1 << i;
if ((j & b) != 0)
{
element.add(set.get(i));
}
}
result.add(element);
}
return result;
}
private static List<Integer> createList(int numberOfElements)
{
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<numberOfElements; i++)
{
list.add(i+1);
}
return list;
}
private static <T> void compute(
HashMap<Integer, List<Set<T>>> powerSet, int numberOfSetsPerNode,
List<List<Set<T>>> prunedNodes)
{
Set<List<Set<T>>> level0 = createLevel0(numberOfSetsPerNode);
System.out.println("Level 0:");
print(level0);
Set<List<Set<T>>> currentLevel = level0;
int level = 0;
while (true)
{
Set<List<Set<T>>> nextLevel =
createNextLevel(currentLevel, powerSet, prunedNodes);
if (nextLevel.size() == 0)
{
break;
}
System.out.println("Next level: "+nextLevel.size()+" nodes");
print(nextLevel);
currentLevel = nextLevel;
level++;
}
}
private static <T> Set<List<Set<T>>> createLevel0(int numberOfSetsPerNode)
{
Set<List<Set<T>>> level0 =
new LinkedHashSet<List<Set<T>>>();
List<Set<T>> level0element = new ArrayList<Set<T>>();
for (int i=0; i<numberOfSetsPerNode; i++)
{
level0element.add(new LinkedHashSet<T>());
}
level0.add(level0element);
return level0;
}
private static <T> List<Set<T>> getNext(Set<T> current, HashMap<Integer, List<Set<T>>> powerSet)
{
ArrayList<Set<T>> ritorno = new ArrayList<Set<T>>();
int level = current.size();
List<Set<T>> listnext = powerSet.get(level+1);
if(listnext != null)
{
for(Set<T> next: listnext)
{
if(next.containsAll(current))
{
ritorno.add(next);
}
}
}
return ritorno;
}
private static <T> Set<List<Set<T>>> createNextLevel(
Set<List<Set<T>>> currentLevel, HashMap<Integer, List<Set<T>>> powerSet,
List<List<Set<T>>> prunedNodes)
{
Set<List<Set<T>>> nextLevel = new LinkedHashSet<List<Set<T>>>();
//Per ogni nodo del livello corrente
for (List<Set<T>> currentLevelElement : currentLevel)
{
//Per ogni insieme del nodo preso in considerazione
for (int i=0; i<currentLevelElement.size(); i++)
{
List<Set<T>> listOfnext = getNext (currentLevelElement.get(i), powerSet);
for (Set<T> element : listOfnext)
{
List<Set<T>> nextLevelElement = copy(currentLevelElement);
Set<T> next = element;
nextLevelElement.remove(i);
nextLevelElement.add(i, next);
boolean pruned = false;
for (List<Set<T>> prunedNode : prunedNodes)
{
if (isSuccessor(prunedNode, nextLevelElement))
{
pruned = true;
}
}
if (!pruned)
{
nextLevel.add(nextLevelElement);
}
else
{
System.out.println("Pruned "+nextLevelElement+ " due to "+prunedNodes);
}
}
}
}
return nextLevel;
}
private static <T> boolean isSuccessor(
List<Set<T>> list, List<Set<T>> successor)
{
for (int i=0; i<list.size(); i++)
{
Set<T> set = list.get(i);
Set<T> successorSet = successor.get(i);
//System.out.println("Successor:" + successorSet + "pruned:" + set);
if (!successorSet.containsAll(set))
{
return false;
}
}
return true;
}
private static <T> List<Set<T>> copy(List<Set<T>> list)
{
List<Set<T>> result = new ArrayList<Set<T>>();
for (Set<T> element : list)
{
result.add(new LinkedHashSet<T>(element));
}
return result;
}
private static <T> void print(
Iterable<? extends Collection<? extends Collection<T>>> sequence)
{
for (Collection<? extends Collection<T>> collections : sequence)
{
System.out.println(" "+collections);
}
}
}
After 4 EDITs and a lot of discussion, it's slowly becoming more clear what the goal of this application is. Indeed, one would have to think about an appropriate formalization, but it finally does not seem to be so difficult.
In contrast to my first answer ( https://stackoverflow.com/a/22092523 ) this new one iteratively computes the next level from the previous level (and the core of this, createNextLevel, is just 10 lines of code).
In the compute method, the pruning that was asked for in "EDIT4" could be integrated into the while loop.
EDIT: Still more requests in the comments. Integrated them. But this is becoming ridiculous. Um den Rest kannst du dich selbst kümmern.
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class HasseDiagramTest2
{
public static void main(String[] args)
{
int numberOfElements = 2;
int numberOfSetsPerNode = 2;
List<Integer> list = createList(numberOfElements);
List<List<Set<Integer>>> prunedNodes =
new ArrayList<List<Set<Integer>>>();
List<Set<Integer>> prunedNode = new ArrayList<Set<Integer>>();
prunedNode.add(Collections.singleton(1));
prunedNode.add(Collections.singleton(1));
prunedNodes.add(prunedNode);
compute(list, numberOfSetsPerNode, prunedNodes);
}
private static List<Integer> createList(int numberOfElements)
{
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<numberOfElements; i++)
{
list.add(i+1);
}
return list;
}
private static <T> void compute(
List<T> elements, int numberOfSetsPerNode,
List<List<Set<T>>> prunedNodes)
{
Set<List<Set<T>>> level0 = createLevel0(numberOfSetsPerNode);
System.out.println("Level 0:");
print(level0);
Set<List<Set<T>>> currentLevel = level0;
int level = 0;
while (true)
{
Set<List<Set<T>>> nextLevel =
createNextLevel(currentLevel, elements, prunedNodes);
if (nextLevel.size() == 0)
{
break;
}
System.out.println("Next level: "+nextLevel.size()+" nodes");
print(nextLevel);
currentLevel = nextLevel;
level++;
}
}
private static <T> Set<List<Set<T>>> createLevel0(int numberOfSetsPerNode)
{
Set<List<Set<T>>> level0 =
new LinkedHashSet<List<Set<T>>>();
List<Set<T>> level0element = new ArrayList<Set<T>>();
for (int i=0; i<numberOfSetsPerNode; i++)
{
level0element.add(new LinkedHashSet<T>());
}
level0.add(level0element);
return level0;
}
private static <T> Set<List<Set<T>>> createNextLevel(
Set<List<Set<T>>> currentLevel, List<T> elements,
List<List<Set<T>>> prunedNodes)
{
Set<List<Set<T>>> nextLevel = new LinkedHashSet<List<Set<T>>>();
for (List<Set<T>> currentLevelElement : currentLevel)
{
for (int i=0; i<currentLevelElement.size(); i++)
{
for (T element : elements)
{
List<Set<T>> nextLevelElement = copy(currentLevelElement);
Set<T> next = nextLevelElement.get(i);
boolean changed = next.add(element);
if (!changed)
{
continue;
}
boolean pruned = false;
for (List<Set<T>> prunedNode : prunedNodes)
{
if (isSuccessor(prunedNode, nextLevelElement))
{
pruned = true;
}
}
if (!pruned)
{
nextLevel.add(nextLevelElement);
}
else
{
// System.out.println(
// "Pruned "+nextLevelElement+
// " due to "+prunedNodes);
}
}
}
}
return nextLevel;
}
private static <T> boolean isSuccessor(
List<Set<T>> list, List<Set<T>> successor)
{
for (int i=0; i<list.size(); i++)
{
Set<T> set = list.get(i);
Set<T> successorSet = successor.get(i);
if (!successorSet.containsAll(set))
{
return false;
}
}
return true;
}
private static <T> List<Set<T>> copy(List<Set<T>> list)
{
List<Set<T>> result = new ArrayList<Set<T>>();
for (Set<T> element : list)
{
result.add(new LinkedHashSet<T>(element));
}
return result;
}
private static <T> void print(
Iterable<? extends Collection<? extends Collection<T>>> sequence)
{
for (Collection<? extends Collection<T>> collections : sequence)
{
System.out.println(" "+collections);
}
}
}
As mentioned in the comments, I'm rather sure that the formalization of what actually should be done is either unclear or plainly wrong. The criterion for comparing the "nodes" does not match the examples. However, once the sorting criterion (in form of a Comparator) has been specified, this should be rather easy to implement.
Here, the criterion for comparing two "nodes" is the sum of the sizes of all sets in the node, which matches the example that was given (although it intuitively does not make sense, because it does not correspond to any real subset relationship....)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class HasseDiagramTest
{
public static void main(String[] args)
{
List<Integer> set = Arrays.asList(1, 2);
List<List<Integer>> powerSet = computePowerSet(set);
List<List<List<Integer>>> combinations =
computeCombinations(powerSet, 2);
Comparator<List<List<Integer>>> comparator = createComparator();
Collections.sort(combinations, comparator);
List<List<List<List<Integer>>>> levels = createLevels(combinations);
for (List<List<List<Integer>>> level : levels)
{
System.out.println(level);
}
}
private static <T> List<List<List<List<T>>>> createLevels(
List<List<List<T>>> sortedCombinations)
{
List<List<List<List<T>>>> levels = new ArrayList<List<List<List<T>>>>();
int previousTotalSize = -1;
List<List<List<T>>> currentLevel = null;
for (int i=0; i<sortedCombinations.size(); i++)
{
List<List<T>> combination = sortedCombinations.get(i);
int totalSize = totalSize(combination);
if (previousTotalSize != totalSize)
{
previousTotalSize = totalSize;
currentLevel = new ArrayList<List<List<T>>>();
levels.add(currentLevel);
}
currentLevel.add(combination);
}
return levels;
}
private static <T> Comparator<List<List<T>>> createComparator()
{
return new Comparator<List<List<T>>>()
{
#Override
public int compare(List<List<T>> list0, List<List<T>> list1)
{
return Integer.compare(totalSize(list0), totalSize(list1));
}
};
}
private static <T> int totalSize(List<List<T>> lists)
{
int totalSize = 0;
for (List<T> list : lists)
{
totalSize += list.size();
}
return totalSize;
}
private static <T> List<List<T>> computePowerSet(List<T> set)
{
List<List<T>> result = new ArrayList<List<T>>();
int numElements = 1 << set.size();
for (int j=0; j<numElements; j++)
{
List<T> element = new ArrayList<T>();
for (int i = 0; i < set.size(); i++)
{
long b = 1 << i;
if ((j & b) != 0)
{
element.add(set.get(i));
}
}
result.add(element);
}
return result;
}
private static <T> List<List<T>> computeCombinations(List<T> list, int sampleSize)
{
int numElements = (int) Math.pow(list.size(), sampleSize);
int chosen[] = new int[sampleSize];
List<List<T>> result = new ArrayList<List<T>>();
for (int current = 0; current < numElements; current++)
{
List<T> element = new ArrayList<T>(sampleSize);
for (int i = 0; i < sampleSize; i++)
{
element.add(list.get(chosen[i]));
}
result.add(element);
increase(chosen, list.size());
}
return result;
}
private static void increase(int chosen[], int inputSize)
{
int index = chosen.length - 1;
while (index >= 0)
{
if (chosen[index] < inputSize - 1)
{
chosen[index]++;
return;
}
chosen[index] = 0;
index--;
}
}
}
So if you have a basic set S = {1, 2}, then K = 2 and the set of subsets of S is {{}, {1}, {2}, {1,2}}. Assume n is still 2. Then your output will be something like
({}, {})
({1}, {}); ({2}, {}); ({}, {1}); ({}, {2})
({1,2}, {}); ({}, {1,2})
({1}, {1}); ({1}, {2}); ({2}, {1}); ({2}, {2})
({1}, {1,2}); ({1,2}, {1}); ({2}, {1,2}); ({1,2}, {2})
({1,2}, {1,2})
Correct? The ordering with the output is a bit difficult because the result isn't fully ordered. But it still boils down to counting. Not, as I initially thought, (K+1)-ary but more (2^K)-ary.
In order to determine if one set is a subset of another, using primes might be an idea.
You assign a prime number to each element of your original set. In my example, that would be 2 and 3. The set of subsets can be build by generating all products of the prime numbers. In my example that would be {1 /* empty set */, 2, 3, 6}.
If you have two sets, represented by your product it is easy to test the inclusion:
if (a % b == 0) then b is a subset of a
It's just a bunch of ideas, but they might help you finding a solution. Of course, the prime trick only works for a relatively small number of elements in your original set, but as soon as K and N grow, you'll get problems anyway. (The number of elements in your output will be (2^K)^N = 2^(NK). If K == N == 5, you'll have 2^(5 * 5) = 2^25, about 32 million output elements. And here the prime thought still works).
Edit: Well I wrote a small Java Program to show my ideas.
save it to Hasse.java
compile it: javac Hasse.java
run it: java Hasse > hasse.dot
run dot: dot -Tpdf -ohasse.pdf hasse.dot
view it: acroread hasse.pdf
Source Code:
import java.lang.*;
import java.util.*;
public class Hasse {
private static int K[] = { 1, 2, 3 };
private static int N = 2;
private static int prime[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
//
// PK[0][] is the array of "subsets"
// PK[1][] is the array of number of elements of K participating in the subset
//
private static int PK[][];
// some constants; the initialization is clear enough
private static final long twoNK = pow(2, N * K.length);
private static final int twoK = (int) pow(2, K.length);
private static final int NK = N * K.length;
private static final long NKf = fac(NK);
//
// this power function isn't suitable for large powers
// but in the range we are working, it's OK
//
public static long pow(int b, int p)
{
long result = 1;
for (int i = 0; i < p; ++i)
result *= b;
return result;
}
// fac calculates n! (needed for the a over b calculation)
public static long fac(int n)
{
long result = 1;
for (int i = n; i > 0; --i) result *= i;
return result;
}
//
// constructPK builds the set of subsets of K
// a subset is represented by a product of primes
// each element k_i of K has an associated prime p_i
// since the prime factorization of a number is unique,
// the product can be translated into a subset and vice versa
//
public static void constructPK()
{
int i, cnt;
int numElms = twoK;
PK = new int[2][numElms];
for (i = 0; i < numElms; ++i) {
int j = i;
cnt = 0;
PK[0][i] = 1;
PK[1][i] = 0;
while (j > 0) {
if (j % 2 == 1) {
PK[0][i] *= prime[cnt];
PK[1][i]++;
}
cnt++;
j /= 2;
}
}
}
// we have a k-ary number (that is: binary if k == 2, octal if k == 8
// and so on
// the addOne() function calculates the next number based on the input
public static void addOne(int kAry[])
{
int i = 0;
kAry[i] += 1;
while (kAry[i] >= twoK) {
kAry[i] = 0;
++i;
kAry[i] += 1;
}
}
// the addN() function is similar to the addOne() function
// with the difference that it add n to the input, not just 1
public static void addN(int kAry[], int n)
{
int i = 0;
kAry[i] += n;
for (i = 0; i < N - 1; ++i) {
while (kAry[i] >= twoK) {
kAry[i] -= twoK;
kAry[i+1] += 1;
}
}
}
// from the k-ary number, which represents a node in the graph,
// the "level" is calculated.
public static int getLevel(int kAry[])
{
int level = 0;
for (int i = 0; i < N; ++i) {
level += PK[1][kAry[i]];
}
return level;
}
// output function for a node
public static String renderNode(int kAry[])
{
StringBuffer sb = new StringBuffer();
String sep = "";
sb.append("(");
for (int i = 0; i < N; ++i) {
String setSep = "";
int p = PK[0][kAry[i]];
sb.append(sep);
sb.append("{");
for (int j = 0; j < K.length; ++j) {
if (p % prime[j] == 0) {
sb.append(setSep + K[j]);
setSep = ", ";
}
}
sb.append("}");
sep = ", ";
}
sb.append(")");
return sb.toString();
}
// This function calculates the numerical representation
// of a node, addressed by its level and position within the level,
// in the k-ary number system
// if there's a more elegant way of finding the node, it would
// largely speed up the calculation, since this function is needed
// for calculating the edges
public static int[] getKAry(int level, int node)
{
int kAry[] = new int[N];
int nodesSoFar = 0;
for (int i = 0; i < N; ++i) kAry[i] = 0;
for (int cnt = 0; cnt < twoNK; ++cnt) {
if (getLevel(kAry) == level) {
if (nodesSoFar == node) {
return kAry;
} else
nodesSoFar++;
}
if (cnt + 1 < twoNK)
addOne(kAry);
}
return null;
}
// this function converts the decimal nodeNumber to
// its k-ary representation
public static int[] getKAry(int nodeNumber)
{
int kAry[] = new int[N];
for (int i = 0; i < N; ++i) kAry[i] = 0;
addN(kAry, nodeNumber);
return kAry;
}
public static String getLabel(int level, int node)
{
int kAry[] = getKAry(level, node);
return (kAry == null ? "Oops!" : renderNode(kAry));
}
public static void printPK()
{
System.out.println("# Number of elements: " + PK[0].length);
for (int i = 0; i < PK[0].length; ++i) {
System.out.println("# PK[0][" + i + "] = " + PK[0][i] + ",\tPK[1][" + i + "] = " + PK[1][i]);
}
}
public static void printPreamble()
{
System.out.println("digraph G {");
System.out.println("ranksep = 3");
System.out.println();
}
public static void printEnd()
{
System.out.println("}");
}
public static void printNodes()
{
int numNodes;
for (int i = 0; i <= NK; ++i) {
int level = i + 1;
numNodes = (int) (NKf / (fac(i) * fac(NK - i)));
for (int j = 0; j < numNodes; ++j) {
System.out.println("level_" + level + "_" + (j+1) + " [shape=box,label=\"" + getLabel(i, j) + "\"];");
}
System.out.println();
}
System.out.println();
}
// having two vectors of "sets", this function determines
// if each set in the ss (small set) vector is a subset of
// the corresponding set in the ls (large set) vector
public static boolean isSubset(int ss[], int ls[])
{
for (int i = 0; i < N; ++i)
if (PK[0][ls[i]] % PK[0][ss[i]] != 0) return false;
return true;
}
// this function finds and prints the edges
// it is called about twoNK times (once for each node)
// therefore performance optimizations have to be done here
public static void printEdges(int level, int node, int nodeNumber)
{
int kAry[] = getKAry(node);
int nlAry[];
int numNodes = (int) (NKf / (fac(level + 1) * fac(NK - level - 1)));
String myNode = "level_" + (level + 1) + "_" + (node + 1);
for (int i = 0; i < numNodes; ++i) {
nlAry = getKAry(level + 1, i);
if (nlAry == null) System.exit(1);
if (isSubset(kAry, nlAry)) {
System.out.println(myNode + " -> level_" + (level + 2) + "_" + (i + 1));
}
}
}
// this function renders the dot file
// first some initial text (preamble),
// then the nodes and the edges
// and finally the closing brace
public static void renderDot()
{
int numNodes;
int nodeNumber = 0;
printPreamble();
printNodes();
for (int level = 0; level < NK; ++level) {
numNodes = (int) (NKf / (fac(level) * fac(NK - level)));
for (int node = 0; node < numNodes; ++node) {
// find the edges to the nodes on the next level
printEdges(level, node, nodeNumber);
++nodeNumber;
}
System.out.println();
}
printEnd();
}
public static void main (String argv[])
{
constructPK();
renderDot();
}
}

Categories

Resources