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 {