12
votes

Quel algorithme à utiliser pour segmenter une séquence de nombres dans n sous-ensembles, afin de minimiser l'écart type de la somme des nombres dans chaque sous-ensemble

Je recherche un algorithme pour segmenter une séquence de nombres positifs en n des sous-séquences, de sorte que l'écart type de la somme des nombres dans chaque sous-ensemble est minimisé.

La commande des chiffres dans chaque ultérieur doit être identique à la commande dans la séquence d'origine

Par exemple:

Supposons que j'ai une séquence {1,1,1,1,1,1,1,1} que je voulais segmenter dans 2 sous-séquences.
Je crois que la solution optimale serait {1,1,1,1,1,1,1}, {10,1}.

La somme de la 1ère suivante est de 6, la somme de la deuxième suivante est de 11
L'écart type des deux nombres est de ~ 3,5, ce qui, à mon avis, est le plus bas possible.

Supposons que j'ai une séquence {4,1,1,1,1,6} que je voulais segmenter en 3 sous-conditions.
Je crois que la solution optimale serait {4}, {1,1,1,1}, {6}
La somme des sous-séquences est de 4, 4 et 6.
L'écart type des 3 chiffres est de ~ 1,15, que je crois que le plus bas possible.

Le meilleur algorithme que j'ai pu proposer était de trouver la somme cumulative de chacun des nombres dans la séquence et de segmenter la séquence à chaque intervalle de [Totalsum / NumSubEqueurs].

Par exemple, étant donné la séquence {4,1,1,1,1,6}, les sommes cumulatives des nombres de chaque séquence sont {4,5,6,7,8,14}. Le total de tous les nombres dans la séquence est de 14, étant donné que je souhaite 3 des sous-séquences, je devrais segmenter la séquence lorsque le total atteint 14/3 = 4,66 et 2 * 14/3 = 9.333333.

Cependant, il n'y a pas de lieu réel dans la séquence où le total cumulatif est égal à 4.66 - le premier total cumulatif est 4, et le total cumulé est le total cumulatif. Ainsi, devrais-je m'arrêter ou devrais-je faire demi-tour? Dans ce cas, l'arrondi jusqu'à 4 donne la solution optimale, mais ce n'est pas toujours le cas. Le mieux que je puisse penser est d'essayer toutes les combinaisons d'arrondir de haut en bas, mais cela se traduit par une complexité O (2 ^ Numsubréquences).

Cela semble être le type de chose qui aurait un algorithme préexistant à appliquer, mais mon googling m'a échoué. Je suis au courant du Problème de partition , qui est NP-Complete, mais qui traite des ensembles non ordonnés, et non ordonné de séquences.

Toute aide serait appréciée.


5 commentaires

Qu'en est-il du {1,1,1,1,1,1,10,2}? Pourriez-vous le partitionner dans {1,1,1,1,1,1,2} et {10} et obtenir un écart-type inférieur? Vous n'avez pas spécifié l'ordre des sous-séquences ou si cela est important.


Oui, la commande doit être préservée. Les sous-séquences doivent être commandées de manière à ce que lorsqu'il s'agisse de concaténée, elles sont égales à la séquence d'origine. Votre exemple ne fonctionnera donc pas, car vous ne pouvez pas concaténer les sous-succédances pour former la séquence d'origine. Une autre façon d'y penser est de trouver N-1 'SplitPoints' dans la séquence d'origine.


Êtes-vous donné un n ou avez-vous besoin de déterminer l'optimal n pour N> 1 (n == 1 1 n'aurait pas de sens puisqu'il n'y a qu'une somme)


Environ ~ 10000 entrées que je veux participer à environ 30 sous-séquences, en moins d'une seconde.


J'ai donné le nombre de subquédances


4 Réponses :


1
votes

Une idée qui me vient à l'esprit consiste à utiliser l'algorithme A * de recherche.

plus à ce sujet: xxx

bon livre à lire à ce sujet: xxx

Certaines choses que vous pouvez utiliser pour la *:

  • État initial: diviser la séquence en n égale (autant que possible) des sous-séquences
  • État suivant: Pour chaque sous-ensemble, ajoutez le numéro de gauche ou de droite (le dernier numéro de sous-ensemble I-1 (si je! = 0) ou le premier numéro de sous-ensemble I + 1 (si je! = N). (Pour créer tous les nœuds descendants du nœud d'état actuel)
  • Heuristic: la valeur optimale serait la moyenne de toutes les valeurs. Son admissible afin qu'il puisse être utilisé avec un *.

    Je ne suis pas sûr que cela vous aidera vraiment avec votre problème, car je n'ai pas résolu ce problème, mais je pense que cela pourrait bien faire. Ce n'est peut-être pas non plus la solution la plus sophistiquée pour ce problème spécifique, mais c'est sûrement meilleur que toute approche "Essayer toutes les combinaisons". Il est également sain et complet (à cause de la heuristique admissible).

    Si vous avez plus de questions à ce sujet et je ferai de mon mieux pour vous aider.


0 commentaires

1
votes

Je pense que vous voulez dire parvenir à des morceaux contigus ou en d'autres termes, trouvez des endroits n-1 sur lesquels couper la séquence en morceaux. (Si vous voulez vraiment dire pour permettre des sous-successes à créer une séquence principale, vous pouvez probablement simplement trier la séquence, résoudre le problème du morceau, puis suivre lorsque les chiffres individuels sont venus de fournir des sous-séquences entrelacées).

Je pense que vous pouvez résoudre ce temps dans le temps proportionnel à N fois la longueur de séquence à l'aide de la programmation dynamique. Travaillez de gauche à droite pour remplir des matrices de bestcost [i] [J] [J] et Lacut [I] [J], où je fonctionne le long de la séquence et J fonctionne de 0 à N-1. BestCost [I] [J] est le coût de la meilleure façon de couper la séquence de 0 à I dans des morceaux J. Lacut [I] [J] est la position de la coupe la plus récente de la coupe qui produit la meilleure [I] [J]. BestCost [I + 1] [J] = Min_k Déviation STD (I + 1 à K) + Bestcost [K - 1] [J - 1]. et puis lutteur [i + 1] [j] = k. À la fin, vous entraînez le coût de la meilleure réponse pour n coupes de la même manière, puis utilisez LacTUCT [] [] pour retracer votre chemin pour trouver les autres coupes.


1 commentaires

Votre solution est bonne, sauf un problème légèrement différent. L'OP veut que l'écart type des sommes des sous-succédances et non de la somme des écarts types des sous-séquences. En outre, cette solution est O (longueur ^ 2 * #Subréquences) Non O (longueur * #subréquences) Par calcul que minimum dans BestCost [i + 1] [J] prend o (longueur) heure. En effet, vous devez exécuter de nombreuses valeurs différentes de k . (Incidemment, j'ai commencé à écrire ma réponse avant de poster le vôtre. Ce n'est pas une coïncidence qu'ils sont similaires car la programmation dynamique est la voie à suivre ici.)



9
votes

Supposons que la longueur de la séquence d'origine est l code> et le nombre de sous-séquences est n code>.

vous pouvez Simplifiez l'expression de l'écart type pour obtenir sqrt (e [x ^ 2] - e [x] ^ 2) code >, où E code> désigne l'attente / moyenne et x code> indique votre variable aléatoire - dans votre cas, la somme des sous-séquences. (Une formule similaire s'applique à "l'exemple d'écart type".) Notez que E [x] code> ne dépend pas de la manière dont vous divisez votre séquence, car ce sera toujours la somme totale divisée par N code>. Ainsi, nous voulons simplement minimiser e [x ^ 2] code> ou équivalent, la somme de x ^ 2 code> (ils diffèrent par un facteur de n code > Par la définition de la moyenne). P>

À ce stade, nous pouvons constater que ce problème peut être résolu avec une programmation dynamique. Laissez f (i, j) code>, pour i code> à partir de 0 code> à m code> et j code > à partir de 1 code> à n code>, soyez la somme minimale des carrés de sujets de sous-séquences de la division du premier i code> éléments de votre séquence dans j code> des sous-séquences. Ensuite, nous voyons que f (i, j) code> peut être calculé en termes de tous les f (i ', j') code> avec i ' et j . Plus spécifiquement, si votre séquence est a [k] code> indexé de 0 code> à m-1 code>: p> xxx Pré>

ayant minimisé f (n, l) code>, vous pouvez utiliser des techniques de programmation dynamique standard pour récupérer les scissions. En particulier, vous pouvez stocker le l code> qui minimise f (i, j) code>. P>

Le temps d'exécution de cette solution est O (l ^ 2 N) code> parce que vous calculez o (ln) code> différentes valeurs de f code> et le minimum code> est sur o ( L) code> Valeurs différentes de l code>. P>

Voici une implémentation simple dans Perl: p>

#!/usr/bin/perl

use strict;
use warnings;

local $\ = $/;
print join ", ", map {"@$_"} best( 2, qw(1 1 1 1 1 1 10 1) );
# prints "1 1 1 1 1 1, 10 1"

print join ", ", map {"@$_"} best( 3, qw(4 1 1 1 1 6) );
# prints "4, 1 1 1 1, 6"

sub best {
    my( $N, @a ) = @_;

    my( @f, @g, $i, $j, $k, $sum );

    # DP base case
    $sum = 0;
    $f[0][1] = $g[0][1] = 0;
    for $i ( 1 .. @a ) {
        $sum += $a[$i-1];
        $f[$i][1] = $sum * $sum;
        $g[$i][1] = 0;
    }

    # DP recurrence
    for $j ( 2 .. $N ) {
        $f[0][$j] = $g[0][$j] = 0;
        for $i ( 1 .. @a ) {
            $sum = 0;
            $f[$i][$j] = $f[$i][$j-1];
            $g[$i][$j] = $i;
            for $k ( reverse 0 .. $i-1 ) {
                $sum += $a[$k];
                if( $f[$i][$j] > $f[$k][$j-1] + $sum * $sum ) {
                    $f[$i][$j] = $f[$k][$j-1] + $sum * $sum;
                    $g[$i][$j] = $k;
                }
            }
        }
    }

    # Extract best expansion
    my( @result );
    $i = @a; $j = $N;

    while( $j ) {
        $k = $g[$i][$j];
        unshift @result, [@a[$k .. $i-1]];
        $i = $k;
        $j--;
    }

    return @result;
}


4 commentaires

Bonne réponse! J'essayais de transformer cela en un problème DP, mais vous l'avez eu d'abord: p


+1 J'ai du mal à apprendre à la programmation dynamique et à utiliser votre réponse comme étude de cas. Toute clarification supplémentaire que vous pouvez ajouter serait appréciée. En particulier - et je ne suis pas certain que c'est une bonne question - existe-t-il une explication intuitive pour les valeurs de la matrice après la fin de la section "DP RECURRENCE" du code est terminée? Merci.


$ f [$ I] [$ j] contient la réponse à un sous-programme: la plus petite somme possible des carrés pour les premières entrées de votre liste, en supposant que vous divisez dans $ j parties. Puis $ g [$ i] [$ j] contient l'emplacement du début de la dernière subdivision. La perspicacité clé qui rend les travaux de programmation dynamiques est que si vous prenez une solution optimale à ce problème et enlevez le dernier groupe (ou premier) de chiffres, vous avez une solution optimale à un problème plus faible. Ainsi, vous pouvez accumuler une solution en résolvant les sous-périllons. C'est également équivalent à la récursion avec la mémoire de mémoisation, donc si vous l'obtenez, vous l'avez presque compris.


@UNE. Rex Merci, cela aide beaucoup.



1
votes

Je conviens que la programmation dynamique peut être la meilleure approche - une technique que je voudrais exclure est une optimisation non linéaire. Vous avez une fonction d'objectif non linéaire si vous minimisez la racine carrée ou simplement la somme des différences au carré. Vous avez également une variables entière dans la partie de votre ensemble de contraintes - l'attribution de membres aux ensembles nécessite des variables entier quelle que soit votre formulation. Une optimisation non linéaire avec des variables entières est généralement très difficile, voire impossible à résoudre de manière optimale. Si vous n'avez besoin que d'une solution approximative, un algorithme génétique pourrait être une bonne approche où la chaîne génétique est une représentation de l'affectation d'un membre à un ensemble.

En ce qui concerne tout cela en moins d'une seconde ... bonne chance!


0 commentaires