Comment puis-je créer un IntStream qui commence au milieu d'une plage séquentielle donnée, puis diffuse les nombres suivants en commençant au milieu et en alternant vers la gauche et la droite. Par exemple, pour une plage séquentielle donnée de nombres 1 2 3 4 5, la séquence personnalisée serait 3 2 4 1 5 ou 3 4 2 5 1 selon que vous commenciez par la gauche ou la droite en premier.

J'essaie essentiellement d'itérer dans un tableau en partant du milieu et en allant vers l'extérieur de manière uniforme (sans aller complètement à gauche ou à droite en premier).

J'ai essayé cela en utilisant uniquement des for boucles, mais le code est compliqué et je pense qu'il serait beaucoup mieux d'aligner simplement une collection ou un flux de nombres au lieu de le vérifier à la volée (à cause de toutes les exceptions d'index hors limites qui doivent être vérifiées). Voici le code original qui, à mon avis, serait bien meilleur en tant que flux d'entrées pré-calculé:

        int middle = myArray.length / 2;
        Object value = myArray[middle]; //have to reference middle since the for loop won't
        //do operation on value
        for (int offset = 1; true; offset++) {
            int nextRight = middle + offset;
            int nextLeft = middle - offset;
            if (nextRight < myArray.length) { // Have to guard against exception, but can't catch exception ahead of time because left or null may not be empty.
                Object value = myArray[nextRight];
                //do operation on value
            }
            if (nextLeft >= 0) {
                Object value = myArray[nextRight];
                //do operation on value
            }
            if (nextRight >= myArray.length) {
                break; //exit logic
            }
            if (nextLeft < 0) {
                break; //exit logic
            }
        }
12
Zombies 30 déc. 2017 à 18:57

7 réponses

Meilleure réponse

Cette solution utilise des itérateurs et des flux:

boolean toLeft = false;
int size = 5;

int half = size % 2 == 0 ? size / 2 : size / 2 + 1;
IntStream inferiorStream = IntStream.iterate (half, x -> x - 1);
IntStream superiorStream = IntStream.iterate (half, x -> x + 1);

OfInt a = toLeft 
          ? inferiorStream.iterator () 
          : superiorStream.iterator ();
OfInt b = toLeft 
          ? superiorStream.skip (1).iterator () 
          : inferiorStream.skip (1).iterator ();

IntStream stream = Stream.generate (() -> IntStream.concat (
    a.hasNext () ? IntStream.of (a.nextInt ()) : IntStream.empty (),
    b.hasNext () ? IntStream.of (b.nextInt ()) : IntStream.empty ()))
    .flatMapToInt (Function.identity ())
    .limit (size);

stream.forEach (System.out :: println);

Sortie (toLeft = true):

3
4
2
5
1

Sortie (toLeft = false):

3
2
4
1
5
2
PaperMonoid 30 déc. 2017 à 19:05

Nous pouvons personnaliser la méthode IntStream.generate pour générer le IntStream dans la séquence dont nous avons besoin

public void run(String... args) throws Exception {
    final int[] arr = IntStream.range(0, 9).toArray(); //test data
    int mid = arr.length / 2;
    SeqGen seq = new SeqGen(arr, mid);
    List<Integer> ints = IntStream.generate(() -> seq.gen())
            .limit(arr.length)
            .boxed()
            .collect(Collectors.toList());
    System.out.println(ints);
}

SeqGen

private class SeqGen {
    int[] arr;
    int start;
    int curr;
    boolean flag;

    public SeqGen(int[] arr, int start) {
        this.arr = arr;
        this.start = start;
    }

    public int gen() {
        int ret = -1;
        int l = arr[start + curr];
        int r = arr[arr.length - curr - start - 1];
        if (!flag) {
            ret = l;
            curr++;
        } else {
            ret = r;
        }
        flag = !flag;
        return ret;
    }
}

Production

[4, 3, 5, 2, 6, 1, 7, 0, 8]
1
Saravana 30 déc. 2017 à 17:48

Puisque vous avez dit que vous voulez utiliser la séquence pour parcourir un tableau, je l'ai modifiée pour produire des nombres comprenant zéro, mais en excluant n , afin que vous puissiez directement passer une longueur de tableau et obtenir des indices valides.

Ensuite, vous pouvez utiliser

static IntStream altSeq(int n) {
    int mid = (n-1)/2;
    return IntStream.rangeClosed(1, n)
                    .map(i -> mid + (i>>>1)*signum(rotateRight(i,1)));
}

L'approche est similaire à la réponse de Stuart Marks, mais utilise un IntStream.rangeClosed() comme base, ce qui crée un dimensionné , ce qui fonctionne beaucoup plus efficacement que de créer un flux infini (comme iterate) et d'appliquer un limit, en particulier pour des opérations comme toArray, count , et pour parallel flux. En d'autres termes, les performances sont comparables à celles d'une itération sur la plage / le tableau dans l'ordre habituel.

La plage de nombres naturels est convertie en utilisant le bit le plus bas comme signe, qui alterne pour les nombres ascendants, et en décalant les nombres d'un bit vers la droite, ce qui équivaut à diviser la grandeur des nombres par deux.

Une notation alternative n'effectuant qu'un décalage de bits par élément serait

static IntStream altSeq(int n) {
    int mid = (n-1)/2;
    return IntStream.rangeClosed(1, n)
                    .map(i -> Integer.rotateRight(i, 1))
                    .map(i -> mid + (i&Integer.MAX_VALUE)*signum(i));
}
2
Holger 2 janv. 2018 à 22:48

Comme vous êtes prêt à envisager une collection, l'approche «low tech» suivante est assez simple:

public static List<Integer> f(int len) {
    int offset = len / 2;

    ArrayList<Integer> indices = new ArrayList<>(len);

    for(int i = 0 ; i < len; i++) {
        int index = offset + i * direction(i);
        indices.add(index);
        offset = index;
    }

    return indices;
}


private static int direction(int size) {
    return (size & 1) == 0 ? 1 : -1;
}

L'appel à f(5), renvoie: [2, 1, 3, 0, 4]

La direction (de gauche à droite ou de droite à gauche) peut être modifiée en changeant direction(). Si vous avez vraiment besoin d'index basés sur 1, modifiez f() pour avoir: indices.add(index+1)

1
David Soroko 31 déc. 2017 à 12:00

Formule utilisée pour générer un flux pour toute plage séquentielle du début à la fin

public static IntStream shuffle(int start, int end){
    int size = end - start + 1;
    int center = start + ((size + 1) >> 1) - 1;
    int even = (size + 1) & 1;
    int direction = 1 - (even << 1); //for left first: (even << 1) - 1;
    return IntStream.range(1, size + 1)
            .map(i -> center + (even + i * direction * ((((i + 1) & 1) << 1) - 1)) / 2);
}
1
Adrian 3 janv. 2018 à 19:59

Eh bien, je peux penser à cela, je ne sais pas si cela correspond à vos besoins:

public static IntStream generate(int[] x, boolean direction) {
    int length = x.length / 2;

    IntStream right = IntStream.range(1, length + 1)
            .mapToObj(i -> {
                return direction ? new Integer[] { i, -1 * i } : new Integer[] { -1 * i, i };
            })
            .flatMap(Arrays::stream)
            .mapToInt(i -> x[length + i]);

    return IntStream.concat(IntStream.of(x[length]), right);
}
2
Eugene 31 déc. 2017 à 07:11

Essaye ça:

import static java.lang.Integer.signum;

static IntStream altSeq(int n) {
    return IntStream.iterate(2 * (n % 2) - 1, i -> -i - signum(i))
                    .map(i -> i / 2 + (n + 1) / 2)
                    .limit(n);
}

Par exemple, altSeq(5) produit:

[3, 2, 4, 1, 5]

Et l'exécution de altSeq(6) produit:

[3, 4, 2, 5, 1, 6]

En bref, nous générons une séquence ascendante qui alterne le signe:

1, -2, 3, -4, 5, ...

C'est ce que fait l'expression i -> -i - signum(i). Ensuite, nous divisons par deux afin d'obtenir les décalages à partir du point médian:

0, -1, 1, -2, 2, ...

C'est de là que vient le i / 2 dans le premier terme de l'expression map. Ensuite, nous ajoutons le milieu de la plage 1..n qui est (n + 1) / 2, le deuxième terme de l'expression map. Pour n = 5, cela nous donne

3, 2, 4, 1, 5, ...

Commencer à 1 fonctionne pour les séquences de longueur impaire. Pour des longueurs paires, nous voulons commencer par -1. L'expression de départ 2 * (n % 2) - 1 calcule la bonne valeur de départ pour les séquences de longueur paire et impaire.

Enfin, nous appliquons limit(n) pour terminer la séquence.

8
Stuart Marks 1 janv. 2018 à 09:48