La principale sfida in questo problema di ottimizzazione è di natura matematica.
L'obiettivo, come posso dedurre dalla definizione del metodo gen_abc
, è di ridurre lo spazio di ricerca individuando intervalli di delimitazione per le varie variabili ($a
, $b
ecc.)
La strategia migliore è quella di estrarre il maggior numero di lineari vincoli dal set completo di vincoli, il tentativo di dedurre i limiti (utilizzando linear programming tecniche, vedi sotto), quindi procedere con esaustivo (o non-deterministico) trial- e-error test contro uno spazio variabile potato.
Un tipico linear programming problem è della forma:
minimize (maximize) <something>
subject to <constraints>
Ad esempio, dato tre variabili, a
, b
e c
, ed i seguenti vincoli lineari:
<<linear_constraints>>::
$a < $b
$b > $c
$a > 0
$c < 30
Potete trovare superiore e limiti inferiori per $a
, $b
e $c
come segue:
lower_bound_$a = minimize $a subject to <<linear_constraints>>
upper_bound_$a = maximize $a subject to <<linear_constraints>>
lower_bound_$b = minimize $b subject to <<linear_constraints>>
upper_bound_$b = maximize $b subject to <<linear_constraints>>
lower_bound_$c = minimize $c subject to <<linear_constraints>>
upper_bound_$c = maximize $c subject to <<linear_constraints>>
In Perl è possibile utilizzare Math::LP a questo scopo.
ESEMPIO
Un vincolo lineare è del tipo "C eqop C1×$V1 ± C2×$V2 ± C3×$V3 ...
", dove
eqop
è uno dei <
, >
, ==
, >=
, <=
$V1
, $V2
ecc sono variabili, e
C
, C1
, C2
ecc sono costanti, possibilmente pari a 0.
Ad esempio, dato ...
$a < $b
$b > $c
$a > 0
$c < 30
... spostare tutto variabili (con i loro coefficienti) a sinistra della disuguaglianza e le costanti solitarie a destra della disuguaglianza:
$a - $b < 0
$b - $c > 0
$a > 0
$c < 30
... e regolare i vincoli in modo che solo =
, <=
e >=
(a) uguaglianze sono utilizzati (assumendo cioè i valori interi discreti per le nostre variabili):
- '... < C' diventa'. .. < = C-1'
- '...> C' diventa' ...> = C + 1'
... cioè,
$a - $b <= -1
$b - $c >= 1
$a >= 1
$c <= 29
...poi scrivere qualcosa del genere:
use Math::LP qw(:types); # imports optimization types
use Math::LP::Constraint qw(:types); # imports constraint types
my $lp = new Math::LP;
my $a = new Math::LP::Variable(name => 'a');
my $b = new Math::LP::Variable(name => 'b');
my $c = new Math::LP::Variable(name => 'c');
my $constr1 = new Math::LP::Constraint(
lhs => make Math::LP::LinearCombination($a, 1, $b, -1), # 1*$a -1*$b
rhs => -1,
type => $LE,
);
$lp->add_constraint($constr1);
my $constr2 = new Math::LP::Constraint(
lhs => make Math::LP::LinearCombination($b, 1, $c, -1), # 1*$b -1*$c
rhs => 1,
type => $GE,
);
$lp->add_constraint($constr2);
...
my $obj_fn_a = make Math::LP::LinearCombination($a,1);
my $min_a = $lp->minimize_for($obj_fn_a);
my $max_a = $lp->maximize_for($obj_fn_a);
my $obj_fn_b = make Math::LP::LinearCombination($b,1);
my $min_b = $lp->minimize_for($obj_fn_b);
my $max_b = $lp->maximize_for($obj_fn_b);
...
# do exhaustive search over ranges for $a, $b, $c
Naturalmente, quanto sopra può essere generalizzato a qualsiasi numero di variabili V1
, V2
, ... (ad esempio $a
, $b
, $c
, $d
, ...), con qualsiasi coefficienti C1
, C2
, ... (ad esempio -1, 1, 0, 123, ecc.) e qualsiasi valore costante C
(ad esempio -1, 1, 30, 29, ecc.) purché sia possibile analizzare le espressioni di vincolo in un corrispondente matrice di rappresentazione come:
V1 V2 V3 C
[ C11 C12 C13 <=> C1 ]
[ C21 C22 C23 <=> C2 ]
[ C31 C32 C33 <=> C3 ]
... ... ... ... ... ...
Applicando l'esempio che ci ha fornito,
$a $b $c C
[ 1 -1 0 <= -1 ] <= plug this into a Constraint + LinearCombination
[ 0 1 -1 >= 1 ] <= plug this into a Constraint + LinearCombination
[ 1 0 0 >= 1 ] <= plug this into a Constraint + LinearCombination
[ 0 0 1 <= 29 ] <= plug this into a Constraint + LinearCombination
NOTA
Come nota a margine, se si esegue (rand
basati su test) non deterministici, si può o non può essere una buona idea per tenere traccia (es in un hash) di cui ($a,$b,$c)
tuple sono già stati testati, per evitare di testare nuovamente, se e solo se:
- il metodo essendo testato è più costoso di una ricerca hash (questo non è il caso con il codice di esempio che hai fornito sopra, ma potrebbe essere o meno un problema con il tuo codice reale)
- l'hash non crescerà in proporzioni enormi (o tutte le variabili sono legate da intervalli finiti, il cui prodotto è un numero ragionevole - in quale caso controllando la dimensione dell'hash può indicare se hai esplorato completamente l'intero spazio o no, oppure puoi cancellare l'hash periodicamente così almeno per un intervallo di tempo alla volta hai qualche collisione det .)
- in ultima analisi, se pensi che quanto sopra potrebbe applicarti, puoi tempo varie opzioni di implementazione (con e senza hash) e vedere se vale la pena implementarle o meno.
La principale sfida in questo problema è di natura matematica. L'obiettivo è di ridurre lo spazio di ricerca individuando intervalli di valori per valori come $ a, $ c, quindi calcolando gli intervalli di delimitazione per variabili dipendenti come $ c. – vladr