Search code examples
javaalgorithmdesign-patternslambdafunctional-interface

Find the positions in which the elements located in these have the least distance between them


I try to achieve the positions in which the elements located in these have the least distance between them? i have this code in java 11:

public class Main {
    public static void main(String[] args) {
        int [] arr= {5, 50, 3, 42, 18, 16, 8, 30, 44};      // Array
        menorD l = new menorD();        
        menorD.MinD(arr);   
    }
}

public class  menorD {

    static int arr_zise;

    public static void minD (int [] arr) {
        arr_zise = arr.length;
        int i, j;
        int minD=0;

        for(i=0;i<arr_zise; i++) {
            for(j=0;j<arr_zise; j++) {
                if(arr[i]!=arr[j]) {
                    minD=arr[i]-arr[j];
                    System.out.print(" i="+ arr[i]+ " j="+ arr[j]+ " minD es: "+Math.abs(min));
                    System.out.println();
                }
            }
        }
    }
}

i try to find this:

arr = {5, 50, 3, 42, 18, 16, 8, 30, 44}

my Dmin would be the difference between the numbers with less distance between them, in this case,

Dmin1 = 5-3 = 2;

Dmin2 = 18-16 = 2;

Dmin3 44-42 = 2;

Without repeating the index of the number in the array. I have made this code but I have difficulty finding what I am looking for.


Solution

  • Ideally when handling data that is logically grouped together, you should abstract away a class to encapsulate it. In your case you want to keep track of every possible distance combination. Each Combination should keep track of:

    1. Indexes of the values.
    2. Values themselves.
    3. Distance.
    4. Which one is low and which one is high.

    Out of these 3. and 4. can be computed from 1. and 2.

        class Combination {
            int indexA, indexB, valueA, valueB;
    
            public Combination(int[] array, int indexA, int indexB) {
                this.indexA = indexA;
                this.indexB = indexB;
                this.valueA = array[indexA];
                this.valueB = array[indexB];
            }
    
            public int getDistance() { ... }
            public int getHigh() { ... }
            public int getLow() { ... }
            public int getHighIndex() { ... }
            public int getLowIndex() { ... }
        }
    

    With such data structure (class) available, you can construct objects for each of the possible combinations (without repetitions of course - pay attention how j starts variably at i + 1 to do not repeat possible combinations):

            List<Combination> combinations = new ArrayList<>();
            for (int i = 0; i < array.length; i++) 
                for (int j = i + 1; j < array.length; j++) 
                    combinations.add(new Combination(array, i, j));
    

    Then using this List of Combination you can compute the minimum distance among them all:

            int min = combinations.stream()
                    .mapToInt(Combination::getDistance)
                    .min().getAsInt();
    

    And finally, you can select those combinations that match the minimum distance previously computed:

            combinations.stream()
                .filter(c -> c.getDistance() == min)
                .forEach(c -> System.out.println(c));
    

    The key is having the Combination class abstracted away in its own encapsulated class, so it can be solely responsible of providing the necessary APIs to inspect a particular combination: indexes, values, distances, high value, low value and even String (toString) representations.

    The following is a full working demo of this approach, run it to get a feeling for it:

    import java.util.ArrayList;
    import java.util.List;
    
    public class MinimumDistance {
        
        public static void main(String[] args) {
            printMinimums(5, 50, 3, 42, 18, 16, 8, 30, 44);
        }
    
        public static void printMinimums(int... array) {
            List<Combination> combinations = new ArrayList<>();
            for (int i = 0; i < array.length; i++) 
                for (int j = i + 1; j < array.length; j++) 
                    combinations.add(new Combination(array, i, j));
            
            int min = combinations.stream()
                    .mapToInt(Combination::getDistance)
                    .min().getAsInt();
            
            combinations.stream()
                .filter(c -> c.getDistance() == min)
                .forEach(c -> System.out.println(c));
        }
        
        static class Combination {
            int indexA, indexB, valueA, valueB;
    
            public Combination(int[] array, int indexA, int indexB) {
                this.indexA = indexA;
                this.indexB = indexB;
                this.valueA = array[indexA];
                this.valueB = array[indexB];
            }
    
            public int getDistance() {
                return getHigh() - getLow();
            }
    
            public boolean isValueAHigh() {
                return valueA > valueB;
            }
    
            public int getHigh() {
                return isValueAHigh() ? valueA : valueB; 
            }
    
            public int getLow() {
                return isValueAHigh() ? valueB : valueA; 
            }
    
            public int getHighIndex() {
                return isValueAHigh() ? indexA : indexB; 
            }
    
            public int getLowIndex() {
                return isValueAHigh() ? indexB : indexA; 
            }
    
            public String toString() {
                return String.format("%d[%d] - %d[%d] = %d", 
                                        getHigh(), getHighIndex(), 
                                        getLow(), getLowIndex(), 
                                        getDistance());
            }
        }
    }
    

    Complete code on GitHub

    Hope this helps.