Почему этот неиспользованный поток влияет на результат? - PullRequest
0 голосов
/ 06 августа 2020

Почему следующая фактически неиспользуемая строка (в методе: getAllDefinedVars ) влияет на конечный результат: List collect = allVars.stream (). Filter (v -> false) .collect (Collectors.toList ());

Если я удалю весь метод и одну строку кода , вызывающий этот метод (первая строка в generateOneSetOfBools ), в итоге я получаю другой результат. Я ожидал бы такого поведения, если бы ...

  1. указанная строка повлияла на список allVars или любую другую переменную
  2. результат потока будет использоваться

Насколько я понимаю, ничего из этого не происходит. Поэтому удаление всего этого метода не должно повлиять на результат.

Чтобы убедиться, вы можете запустить основную программу в первый раз с методом, содержащим поток, и второй раз без этого метода, а затем сравнить результат .

public class PairwiseMain {
    
    private static PairwisePermutator pp = new PairwisePermutator();

    public static void main(String[] args) {
        for(int i = 0; i < 20; i++) {
            pp.permutate(i);
            System.out.println("----------------");
        }
    }

}



import java.util.ArrayList;
import java.util.Arrays;
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;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class PairwisePermutator {
    
    private int n;
    
    public Stream<boolean[]> permutate(int n) {
        this.n = n;
        List<Var> vars = generateRequiredVars(n);
        List<TupleSet> table = generateTable(vars);
        generateCombinations(table, vars);
        return null;
    }
    
    private void generateCombinations(List<TupleSet> table, List<Var> vars) {
        TupleSet start = findStartTupleSet(table);
        if(start == null) {
            return; 
        }else {
            Tuple t = start.tuples.get(0);
            start.setVars(t);
            generateOneSetOfBools(table, vars, new HashSet<>(Arrays.asList(start)));
        }
        System.out.println(Arrays.toString(vars.toArray()));
        resetAllVars(vars);
        generateCombinations(table, vars);
    }

    private void resetAllVars(List<Var> vars) {
        vars.stream().forEach(v -> v.value = null);
    }

    private void generateOneSetOfBools(List<TupleSet> table, List<Var> allVars, Set<TupleSet> start) {
        List<Var> alreadyDefinedVars = getAllDefinedVars(allVars); //REMOVAL OF THIS LINE SHOULD HAVE NO IMPACT
        Map<TupleSet, Integer> relevant = findRelevantTuplesInOtherTupleSets(table, start);
        boolean changes = false;
        boolean existsMultipleOptions = false;
        TupleSet minimalMultipleOptions = null;
        int minimalMultipleOptionsNumber = Integer.MAX_VALUE;
        for(Map.Entry<TupleSet, Integer> entry : relevant.entrySet()) {
            if(entry.getValue() == -1) {
                removeTuple(entry.getKey());
                start.add(entry.getKey());
                changes = true;
            }else if(entry.getValue() == 1) {   
                changes = true;
                removeTuple(entry.getKey(), table, allVars);
                start.add(entry.getKey());
            }else if(entry.getValue() >= 2) {    
                existsMultipleOptions = true;
                if(entry.getValue() < minimalMultipleOptionsNumber) {
                    minimalMultipleOptionsNumber = entry.getValue();
                    minimalMultipleOptions = entry.getKey();
                }
            }
        }
        if(!changes && existsMultipleOptions) {
            removeRandomTuple(minimalMultipleOptions, start);
        }else if(!changes) { 
            setVars(table);
        }
        if(areAllVarsDefined(allVars)) {
            removeMatchingTuples(table);
            return;
        }
        generateOneSetOfBools(table, allVars, start);
    }

    private List<Var> getAllDefinedVars(List<Var> allVars) {
        List<Var> collect = allVars.stream().filter(v -> false).collect(Collectors.toList());
        return collect;
    }

    private void removeMatchingTuples(List<TupleSet> table) {
        for(TupleSet ts : table) {
            boolean v1 = ts.x.value;
            boolean v2 = ts.y.value;
            Tuple toRemove = null;
            for(Tuple t : ts.tuples) {
                if(t.a == v1 && t.b == v2) {
                    toRemove = t;
                }
            }
            if(toRemove != null) {
                ts.setVars(toRemove);
            }
        }
    }

    private boolean areAllVarsDefined(List<Var> allVars) {
        return allVars.stream().filter(v -> v.value != null).count() == n;
    }

    private void removeRandomTuple(TupleSet minimalMultipleOptions, Set<TupleSet> start) {
        Tuple toRemove = minimalMultipleOptions.tuples.get(0);
        minimalMultipleOptions.setVars(toRemove);
        start.add(minimalMultipleOptions);
    }

    private void setVars(List<TupleSet> table) {
        boolean foundOne = false;
        for(TupleSet ts : table) {
            if(ts.x.value == null && ts.y.value == null) {
                foundOne = setVars(ts);
            }
        }
        if(!foundOne) {
            for(TupleSet ts : table) {
                if(ts.x.value == null || ts.y.value == null) {
                    if(ts.tuples.isEmpty()){
                        ts.x.value = true;
                        ts.y.value = false;
                    }else {
                        ts.setVars(ts.tuples.get(0));
                    }
                }
            }
        }
        
    }

    private boolean setVars(TupleSet ts) {
        boolean foundOne;
        if(ts.tuples.isEmpty()){
            ts.x.value = true;
            ts.y.value = false;
            foundOne = true;
        }else {
            ts.setVars(ts.tuples.get(0));
            foundOne = true;
        }
        return foundOne;
    }

    private void removeTuple(TupleSet ts, List<TupleSet> table, List<Var> vars) {
        Tuple toRemove = null;
        if(ts.x.value != null) {
            for(Tuple t : ts.tuples) {
                if(t.a == ts.x.value) {
                    toRemove = t;
                }
            }
        }else if(ts.y.value != null) {
            for(Tuple t : ts.tuples) {
                if(t.b == ts.y.value) {
                    toRemove = t;
                }
            }
        }
        if(toRemove != null)
            ts.setVars(toRemove);
    }

    private void removeTuple(TupleSet ts) {
        boolean v1 = ts.x.value;
        boolean v2 = ts.y.value;
        Tuple toRemove = null;
        for(Tuple t : ts.tuples) {
            if(t.a == v1 && t.b == v2) {
                toRemove = t;
            }
        }
        if(toRemove != null) {
            ts.setVars(toRemove);
        }
    }

    private Map<TupleSet, Integer> findRelevantTuplesInOtherTupleSets(List<TupleSet> table, Set<TupleSet> alreadyVisited) {
        Map<TupleSet, Integer> freedomMap = new HashMap<>();
        for(TupleSet ts : table) {
            if(!alreadyVisited.contains(ts)) {
                int degreeOfFreedom = calculateDegreeOfFreedom(ts);
                freedomMap.put(ts, degreeOfFreedom);            }
        }
        return freedomMap;
    }
    
    private int calculateDegreeOfFreedom(TupleSet ts) {
        List<Var> alreadyDefinedVars = new ArrayList<>();
        if(ts.x.value != null) {
            alreadyDefinedVars.add(ts.x);
        }
        if(ts.y.value != null) {
            alreadyDefinedVars.add(ts.y);
        }
        int degreeOfFreedom = 0;
        if(areBothDefinied(alreadyDefinedVars)) {
            degreeOfFreedom = -1; 
        }else if(isExactlyOneDefinied(alreadyDefinedVars)) {
            Var defined = getDefinedVar(alreadyDefinedVars);
            if(defined == ts.x) {
                degreeOfFreedom = (int) ts.tuples.stream().filter(t -> t.a == ts.x.value).count();
            }else if(defined == ts.y) {
                degreeOfFreedom = (int) ts.tuples.stream().filter(t -> t.b == ts.y.value).count();
            }
        }else if(alreadyDefinedVars.isEmpty()) {
            degreeOfFreedom = ts.tuples.size(); 
        }
        return degreeOfFreedom;
    }

    private Var getDefinedVar(List<Var> alreadyDefinedVars) {
        return alreadyDefinedVars.get(0);
    }

    private boolean isExactlyOneDefinied(List<Var> alreadyDefinedVars) {
        return alreadyDefinedVars.size() == 1;
    }

    private boolean areBothDefinied(List<Var> alreadyDefinedVars) {
        return alreadyDefinedVars.size() == 2;
    }

    private TupleSet findStartTupleSet(List<TupleSet> table) {
        TupleSet startingTupleSet = null;
        for(TupleSet ts : table) {
            if(!ts.tuples.isEmpty()) {
                startingTupleSet = ts;
            }
        }
        return startingTupleSet;
    }

    private List<Var> generateRequiredVars(int n) {
        return IntStream.range(0, n).mapToObj(number -> new Var()).collect(Collectors.toList());
    }
    
    private List<TupleSet> generateTable(List<Var> vars) {
        List<TupleSet> tupleSets = new ArrayList<>();
        int kStart = 1;
        for(int i = 0; i < vars.size(); i++) {
            for(int k = kStart; k < vars.size(); k++) {
                tupleSets.add(getInitSet(vars.get(i), vars.get(k)));
            }
            kStart++;
        }
        return tupleSets;
    }
    
    private TupleSet getInitSet(Var x, Var y){
        return new TupleSet(x, y, (new ArrayList<>(Arrays.asList( new Tuple(true, true),
                                            new Tuple(true, false),
                                            new Tuple(false, true),
                                            new Tuple(false, false)))));
    }
    
    private static class TupleSet{
        Var x;
        Var y;
        ArrayList<Tuple> tuples;
        
        TupleSet(Var x, Var y, ArrayList<Tuple> tuples){
            this.x = x;
            this.y = y;
            this.tuples = tuples;
        }
        
        public void setVars(Tuple t) {
            x.value = t.a;
            y.value = t.b;
            tuples.remove(t);
        }

        @Override
        public String toString() {
            return "["+x+","+y+"]"+tuples;
        }
    }
    
    private static class Var{
        public Boolean value;
        
        @Override
        public String toString() {
            return value == null ? "null" : value.toString();
        }
    }
    
    private static class Tuple{
        public Boolean a;
        public Boolean b;
        
        public Tuple(Boolean a, Boolean b) {
            this.a = a;
            this.b = b;
        }
        
        @Override
        public String toString() {
            return "("+a+","+b+")";
        }
    }

}

Примером воздействия может быть запуск pp.permutate (5) со следующим выводом: С потоком: запустите код в том виде, в котором он был опубликован

[true, true, true, true, true]  
[false, false, false, true, false]  
[false, false, false, false, true]  
[true, true, true, false, false]  
[true, false, false, true, false]  
[false, false, true, true, false]  
[true, true, true, false, false] 

Без потока: удалите только первую строку в generateOneSetOfBool ()

[true, true, true, true, true]  
[false, false, false, true, false]  
[false, false, false, false, true]  
[true, true, true, false, false]  
[false, true, true, true, false]  
[true, false, true, true, false]  
[true, true, false, false, false]  

Оба выхода различаются, например, последний строка

1 Ответ

2 голосов
/ 07 августа 2020

Разница в получаемых вами результатах не имеет ничего общего со строкой List collect = allVars.stream().filter(v -> false).collect(Collectors.toList());. Проблема в том, что ваш алгоритм недетерминирован c. Я взял ваш код и запустил его несколько раз для одного и того же ввода:

for(int i = 0; i < 20; i++) {
    pp.permutate(5);
    System.out.println("----------------");
}

Неважно, была ли это строка, о которой вы говорите, или нет - я получал оба вывода, о которых вы упоминание (появляются только эти два варианта):

[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[true, false, false, true, false]
[false, false, true, true, false]
[true, true, true, false, false]
----------------
[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[false, true, true, true, false]
[true, false, true, true, false]
[true, true, false, false, false]

Я не go построчно просматривал ваш код, поэтому я не уверен, но предполагаю, что некоторые из ваших коллекций не t гарантировать что-либо насчет порядка элементов.

Однако интересно то, что когда я запускаю main несколько раз, порядок, в котором появляются разные версии вывода, всегда будет одинаковым (или, по крайней мере, через 5 раз пробовал). Более того, когда эта одна строка, которую вы упомянули, удаляется, этот порядок изменяется, но остается неизменным между вызовами main. Когда мы добавляем к этому тот факт, что на разных машинах она ведет себя по-разному, я могу сделать вывод, что это может иметь какое-то отношение к тому, как программа размещается в памяти.

...