diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d7428dd..ff892bac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased] - 2022-02-15
+## [Unreleased] - 2022-02-17
### Added
@@ -21,6 +21,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Other
+## [3.1.1] - 2022-02-17
+
+### Fixed
+* Fixed inefficient memory usage in KendallTauDistance (issue #183)
+* Fixed inefficient memory usage in KendallTauSequenceDistance (issue #184)
+* Fixed inefficient memory usage in WeightedKendallTauDistance (issue #185)
+* Fixed inefficiency in WeightedKendallTauDistance related to redundant computation (issue #182)
+
+
## [3.1.0] - 2022-02-15
### Added
diff --git a/src/main/java/org/cicirello/permutations/distance/KendallTauDistance.java b/src/main/java/org/cicirello/permutations/distance/KendallTauDistance.java
index 7034e020..c1009062 100644
--- a/src/main/java/org/cicirello/permutations/distance/KendallTauDistance.java
+++ b/src/main/java/org/cicirello/permutations/distance/KendallTauDistance.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2014, 2015, 2017-2022 Vincent A. Cicirello, .
+ * JavaPermutationTools: A Java library for computation on permutations and sequences.
+ * Copyright (C) 2014, 2015, 2017-2022 Vincent A. Cicirello, .
*
* This file is part of JavaPermutationTools (https://jpt.cicirello.org/).
*
@@ -74,7 +75,7 @@ public int distance(Permutation p1, Permutation p2) {
for (int i = 0; i < arrayP2.length; i++) {
arrayP2[i] = invP1[p2.get(i)];
}
- return countInversions(arrayP2);
+ return countInversions(arrayP2, 0, arrayP2.length-1);
}
@Override
@@ -83,15 +84,21 @@ public int max(int length) {
return (length*(length - 1))>>1;
}
- private int countInversions(int[] array) {
- if (array.length <= 1) return 0;
- int m = array.length >> 1;
- int[] left = Arrays.copyOfRange(array, 0, m);
- int[] right = Arrays.copyOfRange(array, m, array.length);
- int count = countInversions(left) + countInversions(right);
+ private int countInversions(int[] array, int first, int last) {
+ if (last <= first) {
+ return 0;
+ }
+ int m = (first + last) >> 1;
+ return countInversions(array, first, m) + countInversions(array, m+1, last) + merge(array, first, m+1, last+1);
+ }
+
+ private int merge(int[] array, int first, int midPlus, int lastPlus) {
+ int[] left = Arrays.copyOfRange(array, first, midPlus);
+ int[] right = Arrays.copyOfRange(array, midPlus, lastPlus);
int i = 0;
int j = 0;
- int k = 0;
+ int k = first;
+ int count = 0;
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
array[k] = left[i];
@@ -117,5 +124,4 @@ private int countInversions(int[] array) {
}
return count;
}
-
}
\ No newline at end of file
diff --git a/src/main/java/org/cicirello/permutations/distance/WeightedKendallTauDistance.java b/src/main/java/org/cicirello/permutations/distance/WeightedKendallTauDistance.java
index b4cb62be..3aa4ace6 100644
--- a/src/main/java/org/cicirello/permutations/distance/WeightedKendallTauDistance.java
+++ b/src/main/java/org/cicirello/permutations/distance/WeightedKendallTauDistance.java
@@ -100,7 +100,7 @@ public double distancef(Permutation p1, Permutation p2) {
w[arrayP2[i]] = weights[p2.get(i)];
}
- return countWeightedInversions(arrayP2, w);
+ return countWeightedInversions(arrayP2, w, 0, arrayP2.length-1);
}
/**
@@ -115,26 +115,33 @@ public double maxf(int length) {
return maxDistance;
}
- private double countWeightedInversions(int[] array, double[] w) {
- if (array.length <= 1) return 0;
- int m = array.length >> 1;
- int[] left = Arrays.copyOfRange(array, 0, m);
- int[] right = Arrays.copyOfRange(array, m, array.length);
- double weightedCount = countWeightedInversions(left, w) + countWeightedInversions(right, w);
+ private double countWeightedInversions(int[] array, double[] w, int first, int last) {
+ if (last <= first) {
+ return 0;
+ }
+ int m = (first + last) >> 1;
+ return countWeightedInversions(array, w, first, m) + countWeightedInversions(array, w, m+1, last) + merge(array, w, first, m+1, last+1);
+ }
+
+ private double merge(int[] array, double[] w, int first, int midPlus, int lastPlus) {
+ int[] left = Arrays.copyOfRange(array, first, midPlus);
+ int[] right = Arrays.copyOfRange(array, midPlus, lastPlus);
int i = 0;
int j = 0;
- int k = 0;
+ int k = first;
+ double weightedCount = 0;
+ double leftWeights = 0;
+ for (int x = 0; x < left.length; x++) {
+ leftWeights += w[left[x]];
+ }
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
+ leftWeights -= w[left[i]];
array[k] = left[i];
i++;
k++;
} else {
// inversions
- double leftWeights = 0;
- for (int x = i; x < left.length; x++) {
- leftWeights += w[left[x]];
- }
weightedCount += w[right[j]] * leftWeights;
array[k] = right[j];
j++;
diff --git a/src/main/java/org/cicirello/sequences/distance/KendallTauSequenceDistance.java b/src/main/java/org/cicirello/sequences/distance/KendallTauSequenceDistance.java
index cc65b70f..73afc8fb 100644
--- a/src/main/java/org/cicirello/sequences/distance/KendallTauSequenceDistance.java
+++ b/src/main/java/org/cicirello/sequences/distance/KendallTauSequenceDistance.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2018-2021 Vincent A. Cicirello, .
+ * JavaPermutationTools: A Java library for computation on permutations and sequences.
+ * Copyright (C) 2018-2021 Vincent A. Cicirello, .
*
* This file is part of JavaPermutationTools (https://jpt.cicirello.org/).
*
@@ -76,7 +77,6 @@
* Industrial Networks and Intelligent Systems, 7(23), Article e1, April 2020.
*
* @author Vincent A. Cicirello, https://www.cicirello.org/
- * @version 5.13.2021
*/
public final class KendallTauSequenceDistance implements SequenceDistanceMeasurer {
@@ -128,7 +128,7 @@ public int distance(int[] s1, int[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -144,7 +144,7 @@ public int distance(long[] s1, long[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -160,7 +160,7 @@ public int distance(short[] s1, short[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -176,7 +176,7 @@ public int distance(byte[] s1, byte[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -192,7 +192,7 @@ public int distance(char[] s1, char[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -208,7 +208,7 @@ public int distance(String s1, String s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -224,7 +224,7 @@ public int distance(float[] s1, float[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -240,7 +240,7 @@ public int distance(double[] s1, double[] s2) {
int numLabels = USE_HASHMAP ? relabelElementsWithHash(s1,s2,relabeling) : relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -256,7 +256,7 @@ public int distance(boolean[] s1, boolean[] s2) {
int numLabels = relabelElements(s1,s2,relabeling);
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -277,7 +277,7 @@ public int distance(Object[] s1, Object[] s2) {
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
/**
@@ -296,7 +296,7 @@ public int distance(List s1, List s2) {
Bucket[][] buckets = bucketSortElements(relabeling, numLabels);
int[] mapping = mapElements(buckets, relabeling.length);
- return countInversions(mapping);
+ return countInversions(mapping, 0, mapping.length-1);
}
private int[] mapElements(Bucket[][] buckets, int seqLength) {
@@ -752,15 +752,21 @@ private int relabelElements(List s1, List s2, int[][] re
}
// assumes all unique elements
- private int countInversions(int[] array) {
- if (array.length <= 1) return 0;
- int m = array.length / 2;
- int[] left = Arrays.copyOfRange(array, 0, m);
- int[] right = Arrays.copyOfRange(array, m, array.length);
- int count = countInversions(left) + countInversions(right);
+ private int countInversions(int[] array, int first, int last) {
+ if (last <= first) {
+ return 0;
+ }
+ int m = (first + last) >> 1;
+ return countInversions(array, first, m) + countInversions(array, m+1, last) + merge(array, first, m+1, last+1);
+ }
+
+ private int merge(int[] array, int first, int midPlus, int lastPlus) {
+ int[] left = Arrays.copyOfRange(array, first, midPlus);
+ int[] right = Arrays.copyOfRange(array, midPlus, lastPlus);
int i = 0;
int j = 0;
- int k = 0;
+ int k = first;
+ int count = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
array[k] = left[i];
@@ -787,6 +793,7 @@ private int countInversions(int[] array) {
return count;
}
+
// internal data structures below
private static final class Bucket {