dossier sudoku

37
Algorithme s de R´ esolution de Sudoku Yves-Marie Le Gueut, Ronan Kerviche Mai 2009 1 7 9 3 2 8 9 6 5 5 3 9 1 8 2 6 4 3 1 4 7 7 3 1

Upload: nader0

Post on 07-Apr-2018

256 views

Category:

Documents


0 download

TRANSCRIPT

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 1/37

Algorithmes de Resolution de Sudoku

Yves-Marie Le Gueut, Ronan Kerviche

Mai 2009

1 7 93 2 8

9 6 55 3 9

1 8 26 4

3 14 7

7 3

1

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 2/37

Table des matieres

1 Qu’est ce qu’un Sudoku 3

2 Un premier algorithme de resolution 4

3 La Programmation Lineaire 5

3.1 Un probleme lineaire . . . . . . . . . . . . . . . . . . . . . . . 53.2 Un premier exemple . . . . . . . . . . . . . . . . . . . . . . . 63.3 L’algorithme du Simplexe . . . . . . . . . . . . . . . . . . . . 103.4 La methode du simplexe dans le cas du premier exemple 113.5 Second exemple et limites . . . . . . . . . . . . . . . . . . . . 133.6 Programmation lineaire en nombre entiers . . . . . . . . . 16

3.6.1 Representation du probleme . . . . . . . . . . . . . . 16

3.6.2 Relaxation continue du probleme . . . . . . . . . . . 163.6.3 Algorithme ”Branch and Bound” . . . . . . . . . . . 163.6.4 Application de l’algorithme ”Branch and Bound” 173.6.5 Algorithme ”Cutting Planes” . . . . . . . . . . . . . 193.6.6 Dans la pratique . . . . . . . . . . . . . . . . . . . . . . 19

4 La Resolution d’un sudoku comme un probleme lineaire 20

4.1 La modelisation du probleme . . . . . . . . . . . . . . . . . . 204.2 Le programme . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.2.1 Le principe general . . . . . . . . . . . . . . . . . . . . 214.2.2 Quelques resultats . . . . . . . . . . . . . . . . . . . . . 24

5 Conclusion 25

6 Bibliographie 25

7 Annexes 25

2

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 3/37

1 Qu’est ce qu’un Sudoku

Le Sudoku est un casse-tete d’origine americaine qui a ete popularise auJapon. Il consiste en une grille carree de 81 chiffres, 9 colonnes et 9 lignesdonc, separee en 9 regions elle-meme carree et contenant 9 cases. Cette grilleest initialement pre-remplie avec une vingtaine de chiffre compris entre 1 et 9(ce nombre varie generalement avec la difficulte de la grille, qui sera abordeeplus loin). Le joueur doit alors remplir les cases laissees vides en utilisantd’une part, uniquement les chiffres de 1 a 9 et d’autre part, en suivant cesquelques regles :– Chaque case ne doit contenir qu’un seul chiffre parmi 1,...,9– Chaque ligne ne doit contenir qu’une seul fois un chiffre donne parmi 1,...,9– Chaque colonne ne doit contenir qu’une seul fois un chiffre donne parmi

1,...,9

– Chaque ’bloc’ (ou sous-region) ne doit contenir qu’une seul fois un chiffredonne parmi 1,...,9

3 55 4 6 2

2 7 1 3 6

7 4 2 35 1 3 7

4 7 9 1

4 6 9 1 51 6 8 7

5 4

Fig. 1 – Un Sudoku initialement prepare

En appliquant les regles precedantes et en usant d’un peu de logique onobtient :

4 6 1 3 7 2 8 5 99 3 5 4 8 6 1 7 2

2 7 8 9 1 5 3 6 4

7 9 4 2 3 1 5 8 65 1 2 8 6 9 4 3 7

6 8 3 5 4 7 9 2 1

8 4 6 7 9 3 2 1 51 2 9 6 5 8 7 4 33 5 7 1 2 4 6 9 8

Fig. 2 – L’unique solution du sudoku precedent, les caracteres en gras etaientdeja disposes

Signalons que, dans le sens commun, un sudoku propose ne doit avoirqu’une unique solution. De plus, bien que tous les sudoku soient soumis auxmemes regles ils n’ont pas tous la meme difficulte. Nous reviendrons sur cepoint.

3

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 4/37

2 Un premier algorithme de resolution

Pour resoudre un tel casse-tete de facon automatisee la premiere methodeenvisagee et de calquer la strategie de l’algorithme sur celle du joueur. C’est adire que l’algorithme va utiliser la meme logique. Nous pouvons donc definirtrois strategies simples.

Nous allons donc, tout d’abord, representer un sudoku comme une matricede M9(1, 9) lie a une matrice de listes de meme taille, cette derniere ayantpour fonction de stocker les possibilites de chaque case.

Une premiere fonction va s’occuper de parcourir cette liste pour trouver leslistes n’ayant qu’un unique element, c’est a dire les cases ou on l’on connaitl’unique possibilite. En placant ce chiffre on repercute le changement sur lescases non encore determinee qui se trouve sur la meme ligne, la meme colonneet le meme bloc que la case courante et enlevant de leurs listes respectives la

possibilite correspondante (si elle existe).Cependant, cette fonction seule ne permet pas de resoudre tous les sudoku.

Ce fait permet d’etablir un premier niveau de difficulte. On definit donc troisnouvelles fonctions qui vont utiliser une nouvelle strategie : elles vont, enconsiderant respectivement une ligne, une colonne ou un bloc, definir si unchiffre ne possede qu’une occurence possible parmi toutes les cases non encorefixees.

De meme, ces nouvelles fonctions alliee a la premiere permettront deresoudre plus de sudoku que cette derniere seule, mais pas tous. Certainssudoku presentent la particularite de mener a un choix. C’est a dire, que pourune case donnee, les 4 strategies precedentes ne permettent de decider lasolution. On utilise alors un algorithme arborescent visant a tester toutes les

possiblites offertes. Ainsi, lorsque les 4 strategies basiques ne permettent plusd’avancer dans la resolution, on va prendre une des cases non encore fixees etpossedant le moins de possibilites parmi toutes les cases. On construit unarbre par la racine en creant autant de branche contenant de nouveaux sudokuque de possibilites et placant dans chacun de ceux-ci une de ces possibilites.On resoud alors chaque sudoku en utilisant les 4 strategies precedantes eteventuellement, en reoperant un ou plusieurs choix si necessaire.

Cet algorithme arrivera a completer n’impote quel sudoku, pourvu qu’il soitcorrect, en un temps fini. Il permettra, de plus, d’enumerer toutes les solutionspossibles pour un sudoku. Cependant, il sera mis a rude epreuve face acertains sudoku de bon niveau. On notera que si l’on passe a cet algorithme unsudoku entierement vide il retournera en un temps fini l’ensemble des sudokupossibles (car il se contente de les enumerer et il a ete montre par Bertram

Felgenhauer et Frazer Jarvis en 2005 que le nombre de sudoku etait de9! ∗ 722 ∗ 27 ∗ 27704267971 ≈ 6.67 ∗ 1021).

Des strategies alternatives existent pour avancer la resolution et repousserun choix (voire l’annihiler dans certains cas). Cependant ces strategies sontutilisees de facon dynamique par les algorithmes les plus communs et sontplutot mineure dans la resolution.

Notre problematique se transforme alors en : peut-on donner une reponse

a ce probleme polynomial sans avoir a ne jamais enumerer et tester de

pseudo-solutions ? 

4

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 5/37

3 La Programmation Lineaire

3.1 Un probleme lineaire

Un probleme lineaire est un probleme pose sous cette forme :

n, m ∈ N

f  : Rn → R

(x1, x1, . . . , xn) → f (x1, x1, . . . , xn)

P  :

Maximiser : f (x1, x1, . . . , xn) = c1 ∗ x1 + c2 ∗ x2 + . . . + cn ∗ xn

Contraintes : a1,1 ∗ x1 + a1,2 ∗ x2 + . . . + a1,n ∗ xn ≤ b1

a2,1 ∗ x1 + a2,2 ∗ x2 + . . . + a2,n ∗ xn ≤ b2...am,1 ∗ x1 + am,2 ∗ x2 + . . . + am,n ∗ xn ≤ bm

Limites : x1 ≥ 0x2 ≥ 0

...xn ≥ 0

La fonction lineaire f  est appelee fonctionnelle ou fonction objectif duprobleme. En ecriture matricielle on peut donner :

P  :

Maximiser : tC ∗ X 

Contraintes : A ∗X ≤ BLimites : X ≥ 0

X  =

x1

...xn

C  =

c1...

cn

B =

b1...

bm

Am,n =

a1,1 a1,2 · · · a1,na2,1 a2,2 · · · a2,n

......

. . ....

am,1 am,2 · · · am,n

5

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 6/37

3.2 Un premier exemple

Un agriculteur cultivant du maıs et du ble cherche maximiser son revenu.Il cherche combien de kilos de semences il doit prevoir. Chaque kilo de maıs lui 

rapportera 10 e et chaque kilo de ble lui rapportera 7 e . Chaque kilo de maıs

occupe 7 ares et demande 3 litres de fertilisant et chaque kilo de ble occupe 3 

ares et demande 5 litres de fertilisant.

Question : combien de kilos de maıs et de ble l’agriculteur doit-il prvoir 

pour maximiser son revenu ? 

On peut mettre ce probleme sous la forme d’un probleme lineaire :a : nombre de kilo de maısb : nombre de kilo de ble

P  :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 270Limites : a ≥ 0

b ≥ 0

Ou encore :

P  :

Maximiser : t

107

ab

Contraintes :

7 33 5

ab

300270

Limites :

ab

00

Sous une forme graphique, on peut representer les contraintes et ledomaine des solutions dites ”acceptables”, c’est a dire : qui ne violent pas lescontraintes posees (Figure 1 et Figure 2).

Dans ce cas precis on peut resoudre le probleme en calculant l’intersectiondes deux droites delimitant le domaine. La solution est unique car, comme onpeut le constater sur le graphique de la Figure 3 l’equation de la fonctionobjectif n’est pas parallele a une des equations contraintes.

On trouve :

a = 26.5385, b = 38.0769

Dans ce cas

f (26.5385, 38.0769) = 531.923

Reponse au probleme : Il faut planter 26.53 kilos de maıs et 38.07 kilos de

ble pour maximiser les revenus, en gagnant 531.92 e.

6

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 7/37

Fig. 3 – Contraintes du probleme P

Fig. 4 – Domaine des solutions recevables du probleme P

7

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 8/37

Fig. 5 – Superposition du domaine precedant avec certaines valeurs de la fonc-tionnelle

8

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 9/37

Fig. 6 – Maximum de la fonctionnelle

9

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 10/37

3.3 L’algorithme du Simplexe

On note E l’ensemble des situations accaptables, c’est a dire, l’ensembledes points dans Rn satisfaisant les equations contraintes. Avec les notationsprecedentes :

E  = {X ∈ Rn/A ∗X ≤ B}

On peut facilement montrer que cet ensemble solution est une partieconvexe de Rn :

∀X, Y  ∈ E, ∀a ∈ [0, 1], (1 − a) ∗ X + a ∗ Y  ∈ E 

Demonstration :

∀X, Y  ∈ E, ∀a ∈ [0, 1],A ∗ ((1− a) ∗X + a ∗Y ) = (1− a) ∗A ∗X + a ∗A ∗Y  ≤ (1− a) ∗B + a ∗B = B

A ∗ ((1 − a) ∗X  + a ∗ Y ) ≤ B ⇒ ((1 − a) ∗X  + a ∗ Y ) ∈ E 

De plus, l’ensemble des solutions forme un polyhedre dans l’espace Rn,comme espace convexe delimite par un nombre fini d’hyperplans.

La clef de l’algorithme du Simplexe telle que formule par G. Dantzig est lesimple parcours du polytope delimitant l’espace E  des solutions comme suit :

Fig. 7 – Exemple de parcours de l’algorithme pour un probleme a deux variables

10

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 11/37

L’algorithme part d’une solution acceptable (ici : (0; 0)) qui est un

sommet et choisit le prochain sommet comme ayant une arrete commune avecle sommet courant et permettant de maximiser la fonction objectif. Sil’algorithme ne peut determiner un tel sommet alors une solution est trouvee.

On notera que si la droite fonction ob jective maximale est parallele a ladroite representant l’equation d’une contrainte alors il existe une infinite desolutions, toutes reparties sur le segment forme par deux sommets pour lesquels la maximisation est atteinte.

Enfin, l’algorithme utilise une expression matricielle du probleme enadaptant a chaque sommet rencontre une nouvelle base de l’espace,permettant de traduire le prochain sommet cible permettant de maximiser lafonctionnelle (si besoin est).

3.4 La methode du simplexe dans le cas du premierexemple

Rappel de la forme du probleme :

P  :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 270Limites : a ≥ 0

b ≥ 0

On introduit alors deux variables d’ecarts x1 et x2 dans la modelisationdes contraintes et on utilise la forme duale du probleme (la forme primale duprobleme etant liee a sa maximisation) :

P  :

Minimiser : p = 10 ∗ a + 7 ∗ b + 0 ∗ x1 + 0 ∗ x2Contraintes : 7 ∗ a + 3 ∗ b + x1 = 300

3 ∗ a + 5 ∗ b + x2 = 270Limites : a ≥ 0 b ≥ 0 x1 ≥ 0 x2 ≥ 0

On peut alors formuler le probleme sous une forme matricielle :

T 0 :

Variables a b x1 x2 p BContrainte 1 (L1) 7 3 1 0 0 300Contrainte 2 (L2) 3 5 0 1 0 270Fonctionnelle (L3) -10 -7 0 0 1 0

Les variables x1 et x2 sont dites en base, tandis que les variables a et b sontdites hors base.

Premiere iteration :Choix de la colonne pivot : on selectionne dans la derniere ligne, la ligne

representant la fonctionnelle, le nombre negatif dont la valeur absolue est laplus grande. Ici −10 satisfait ces conditions, on choisit donc la colonne de lavariable a.

Choix du pivot : On observe les coefficients des contraintes de la colonnepour la variable a : ils sont 7 et 3. On cherche a augmenter la variable a dansles limites de ces contraintes :

11

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 12/37

C 0 :

7 ∗ a ≤ 300 ⇔ a ≤ 300

73 ∗ a ≤ 270 ⇔ a ≤ 2703

Limites : a ≥ 0

La contrainte la plus restrictive est bien entendu :

a ≤300

7

On choisit de ce fait 7 comme pivot et on applique les transformationssuivantes :

L2 ← 7 ∗ L2 − 3 ∗ L1

L3 ← 7 ∗ L3 + 10 ∗ L1

On obtient le nouveau tableau :

T 1 :

Variables a b x1 x2 p BContrainte 1 (L1) 7 3 1 0 0 300Contrainte 2 (L2) 0 26 -3 7 0 990Fonctionnelle (L3) 0 -19 10 0 7 3000

On fait sortir x1 de la base et rentrer a en base.Seconde iteration :

On reitere ce meme procede : on choisit cette fois la colonne de la variableb, c’est la seule qui possede un coefficient strictement negatif (−19).

Les contraintes sont maintenant :

C 1 :

3 ∗ b ≤ 300 ⇔ b ≤ 10026 ∗ b ≤ 990 ⇔ b ≤ 495

13Limites : b ≥ 0

La contrainte la plus restrictive est maintenant :

b ≤495

13

On choisit de ce fait 26 comme pivot et on applique les transformationssuivantes :

L1 ← 26 ∗ L1 − 3 ∗ L2

L3 ← 26 ∗ L3 + 19 ∗ L1

On obtient le nouveau tableau :

T 2 :

Variables a b x1 x2 p BContrainte 1 (L1) 182 0 35 -21 0 4830Contrainte 2 (L2) 0 26 -3 7 0 990Fonctionnelle (L3) 0 0 203 133 182 96810

On fait sortir x2 de la base et rentrer b en base.Il n’y a plus de coefficient strictement negatif dans l’expression de la

fonctionnelle : la solution optimale a ete atteinte. Les variables a et b sont enbase, on trouve :

182 ∗ a = 4830 ⇔ a =4830

182≈ 26.54

12

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 13/37

26 ∗ b = 990 ⇔ b =990

26≈ 38.08

Les variables x1 et x2 sont hors base, on a :

x1 = 0

x2 = 0

De meme, la fonctionnelle a pour valeur :

0 ∗ a + 0 ∗ b + 203 ∗ x1 + 133 ∗ x2 + 182 ∗ p = 98610 ⇒ p =96810

182≈ 531.92

Il faut donc prevoir 26.54 kilos de maıs et 38.08 kilos de ble pour maximiserle revenu, qui vaudra 531.92e.

3.5 Second exemple et limites

Une usine produit deux sortes de jouets : des petits trains vendus 10 e et 

des voitures vendues 7 e . Chaque trains est fabrique partir de 7 unites de bois

et 3 unites de fer, chaque voiture est fabriquee partir de 3 unites de bois et 5 

unites de fer. L’usine dispose de 300 unites de bois et 270 unites de fer.

Question : combien de voitures et de trains doit fabriquer l’usine pour 

 faire un profit maximum ? 

On peut mettre ce probleme sous la forme d’un probleme lineaire :a : quantite de trains produites

b : quantite de voitures produites

Fig. 8 – Superposition du domaine precedant avec certaines valeurs de la fonc-

tionnelle

Ce probleme est identique au probleme precedant... mais la solutionobtenue n’est pas acceptable : les nombres de jouets a produires ne sont pasdes nombres entiers ! Representons maintenant les solution entieres :

13

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 14/37

Fig. 9 – Solutions Entieres (1)

Dans cet exemple simple on remarque une solution sur uneequation-contrainte. On trouve :

a = 27, b = 37

Dans ce cas :

f (27, 37) = 529

14

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 15/37

Fig. 10 – Solutions Entieres (2)

15

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 16/37

On a donc montre que l’algorithme du Simplexe ne peut pas s’adapter

dans sa forme premiere au probleme en nombres entiers ou en booleens. Onutilise donc un second algorithme pour trouver une solution entiere !

3.6 Programmation lineaire en nombre entiers

3.6.1 Representation du probleme

On represente en ecriture matricielle la repartition discrete de solutions par :

P  :

Maximiser : tC ∗ X Contraintes : A ∗X ≤ BLimites : X ≥ 0

Avec :

X  =

x1

...xn

∈ Nn C  =

c1...

cn

∈ Rn B =

b1...

bm

∈ Rm

Am,n =

a1,1 a1,2 · · · a1,na2,1 a2,2 · · · a2,n

......

. . ....

am,1 am,2 · · · am,n

∈ M m,n(R)

3.6.2 Relaxation continue du probleme

On considere tout d’abord l’ensemble des variables comme un ensemblecontinu :∀i ∈ [|1, n|], xi ∈ R

On resoud alors le systeme grace a l’algorithme du simplexe pour trouverune solution acceptable, sommet du polytope delimite par l’ensemble deshyperplans-contraintes. On dispose d’un vecteur solution de P s :

X s ∈ Rn

On cherche a present a obtenir une solution dans Nn.

3.6.3 Algorithme ”Branch and Bound”

Ce premier algorithme consiste a construire un arbre de solution danslequel on evolue en verifiant la maximisation du probleme et en utilisant larelaxation continue precedante. Nous allons donc decrire rapidement cetalgorithme dans le cas d’un probleme en nombres binaires :– Etape Initiale : on considere un probleme : on le note P 0, il est identique a

P s au domaine discret de solution pres. On note V 0 l’ensemble des variablesde ce probleme, elles n’ont pas de valeurs assignee.

– On construit un arbre par la racine et on le dote de deux fils : un pourlequel une variable x de V 0 se voit assigner la valeur 0, l’autre pour lequel lameme variable x de V 0 se voit assigner la valeur 1. On note le problemeassocie : P 1 ; et l’ensemble des variables ”libres” : V 1 (ie. V 1 = V 0 \ {x}.

16

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 17/37

– Pour chaque noeud de cet arbre, de probleme P i et d’ensemble de variables

V i :– Si la solution de la relaxation continue du probleme associe a ce noeud estpire, en terme de maximisation de la solution objectif, que la meilleuresolution entiere trouvee jusqu’ici alors on remonte au dernier noeud nontotalement explore et on supprime la branche que l’on vient de quitter (enanglais backtracking ).

– Si la solution est entierement definie dans Bn on la compare a la meilleuresolution entiere trouvee jusqu’ici et on conserve celle des deux qui est lameilleure.

– Sinon, on considere une variable x de l’ensemble V i et on ajoute au noeudcourant les deux fils pour les quels x a soit pris la valeur 0, soit la valeur1. Donc ces deux noeuds fils sont muni de P i+1 et de V i+1 = V i \ {x}.

– La solution entiere est donc la solution trouvee.

3.6.4 Application de l’algorithme ”Branch and Bound”

On reprend le probleme lineaire precedent :

P  :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 270Limites : a ∈ N, b ∈ N

Par la relaxation continue on avait obtenu cette solution :

a = 26.5385, b = 38.0769

Dans ce cas, la valeur de la fonctionnelle est :

f (26.5385, 38.0769) = 531.923

On choisit d’abord la variable, parmi a et b, dont la valeur est la pluseloignee d’un entier. Ici c’est a qui remplit ce role. On va donc proposer deuxnouveaux problemes en a joutant a chacun une contrainte decoulant ducaractere entier que doit respecter a. On note :

P 1 :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 2701 ∗ a + 0 ∗ b ≤ 26 (C 1)

Limites : a ∈ N, b ∈ N

P 2 :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 2701 ∗ a + 0 ∗ b ≥ 27 (C 2)

Limites : a ∈ N, b ∈ N

Les problemes P 1 et P 2 vont etre resolus separement par l’algorithme duSimplexe. Les contraintes C 1 et C 2 traduisent le fait que a ne peut etre dansl’intervalle ouvert ] 26, 27[.

17

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 18/37

En resolvant P 1, on obtient :

a = 26, b = 38.4, f (26, 38.4) = 528.8

On remarque b n’est pas entier. On choisit cette variable et on applique denouveau la methode precedente :

P 3 :

Maximiser : 10 ∗ a + 7 ∗ bContraintes : 7 ∗ a + 3 ∗ b ≤ 300

3 ∗ a + 5 ∗ b ≤ 2701 ∗ a + 0 ∗ b ≤ 26 (C 1)0 ∗ a + 1 ∗ b ≤ 38 (C 3)

Limites : a ∈ N, b ∈ N

P 4 :

Maximiser : 10 ∗ a + 7 ∗ b

Contraintes : 7 ∗ a + 3 ∗ b ≤ 3003 ∗ a + 5 ∗ b ≤ 2701 ∗ a + 0 ∗ b ≥ 27 (C 2)0 ∗ a + 1 ∗ b ≥ 39 (C 4)

Limites : a ∈ N, b ∈ N

En resolvant P 3, on obtient :

a = 26, b = 38, f (26, 38.4) = 526

On constate que P 3 a une solution entiere.En resolvant P 4, on obtient :

a = 25, b = 39, f (26, 38.4) = 523

On constate que P 4 a une solution entiere. De plus, la fonctionnelle estmaximum pour la solution du probleme P 3 par rapport a la solution duprobleme P 4. On peut donc conserver celle-ci.

En resolvant P 2, on obtient :

a = 27, b = 37, f (27, 37) = 529

On constate que P 2 est une solution entiere au probleme. De plus, lafonctionnelle est maximale pour la solution du probleme P 2 par rapport a lasolution du probleme P 3. On peut donc conserver la solution du probleme P 2qui est la solution entiere optimale du probleme P . On verifie, bien

evidemment, que cette solution correspond a celle trouvee ”graphiquement ”.On peut representer l’arbre construit pendant la recherche :

18

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 19/37

Fig. 11 – Algorithme ”Branch and Bound” : Arbre construit

3.6.5 Algorithme ”Cutting Planes”

Cet algorithme reside dans l’ajout de nouvelle contraintes par laprocedure suivante :– Resoudre la relaxation du probleme lineaire– Si la solution est entiere : terminer le processus– Sinon : Ajouter une contrainte lineaire qui permet de retirer la solution de la

relaxation SANS  retirer de solution entiere.

Il existe pour cela, des methodes permettant de determiner de tels planssecants a partir des contraintes deja utilisees : les Coupes de Gomory.

3.6.6 Dans la pratique

En fait, ces deux techniques sont utilisees conjointement car : les coupesde Gomory ne permettent pas de converger rapidement  vers la solution (encomparaison de la methode arborescente seule). Cependant leur combianaisonpermet de ne pas avoir a explorer certaines branches ouvertes par l’algorithme”Branch and Bound”.

19

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 20/37

4 La Resolution d’un sudoku comme un

probleme lineaire

4.1 La modelisation du probleme

On a vu qu’un Sudoku pouvait etre representer par une matrice carree de81 elements. On peut aussi le representer sous la forme :

∀(i,j,k) ∈ [|1; 9|]3, ψi,j,k ∈ B

ψi,j,k =

1 si la case de coordonnees (i, j) contient le nombre k

0 sinon

Avec la representation precedante on peut ecrire l’ensembles descontraintes d’un sudoku comme sujet a ensemble de contraintes :

S  :

Contraintes : ∀(i, j) ∈ [|1, 9|]2,9

k=1

ψi,j,k = 1

∀(i, k) ∈ [|1, 9|]2,9

j=1ψi,j,k = 1

∀( j,k) ∈ [|1, 9|]2,9

i=1ψi,j,k = 1

∀(i0, j0) ∈ [|0, 2|]2,3

i=1

3j=1

ψ3i0+i,3j0+j,k = 1

Limites : ∀(i,j,k) ∈ [|1; 9|]3, ψi,j,k ∈ B

Puis, on peut alors decrire un probleme lineaire associe :

S l :

Maximiser :

(i,j,k)∈[|1;9|]3ψi,j,k (1)

Contraintes : ∀(i, j) ∈ [|1, 9|]2,9

k=1

ψi,j,k ≤ 1 (2)

∀(i, k) ∈ [|1, 9|]2,9

j=1

ψi,j,k ≤ 1 (3)

∀( j,k) ∈ [|1, 9|]2,9

i=1ψi,j,k ≤ 1 (4)

∀(i0, j0) ∈ [|0, 2|]2,3

i=1

3

j=1ψ3i0+i,3j0+j,k ≤ 1 (5)

Limites : ∀(i,j,k) ∈ [|1; 9|]3, ψi,j,k ∈ B (6)

Clarifions les inegalites apparaıssant ci-dessus :

1. Fonctionnelle a maximiser : il doit y avoir un maximum de ’1’ dans leSudoku

2. Contrainte : il ne doit y avoir qu’un seul chiffre dans chaque case.

3. Contrainte : il ne doit y avoir qu’un seul chiffre donne parmi [|1, 9|] surchaque ligne.

4. Contrainte : il ne doit y avoir qu’un seul chiffre donne parmi [|1, 9|] surchaque colonne.

20

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 21/37

5. Contrainte : il ne doit y avoir qu’un seul chiffre donne parmi [|1, 9|] dans

chaque bloc de 3x3.6. Limite : chaque variable est binaire.

4.2 Le programme

4.2.1 Le principe general

Le programme charge de resoudre les sudoku utilise la bibliothequeGLPK, acronyme de GNU Linear Programming Kit , qui implemente lamethode du Simplexe et permet la resolution de problemes lineaires ennombres entiers.

Le programme doit charger un fichier texte contenant le sudoku aresoudre et construire la structure de donnee adequate. Nous pouvons

representer celle-ci par un tableau tri-dimensionnel :

Fig. 12 – Structure des donnees

Pour chaque chiffre connu, le programme doit ”fermer” les possibilitessimilaires. C’est a dire qu’il doit mettre a FAUX  les possibilites du chiffre dansles zones suivantes : la ligne, la colonne et le bloc auxquels appartient lechiffre. De plus, il doit aussi fermer la possibilite de mettre un autre chiffredans la case. Enfin il met le chiffre considere a VRAI . Il recommence ceprocede pour chaque chiffre lu.

21

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 22/37

Fig. 13 – Structure des donnees : possibilites supprimees au chargement (1)

22

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 23/37

Fig. 14 – Structure des donnees : possibilites supprimees au chargement (2)

Enfin il construit le probleme lie aux cases inconnues et aux contraintess’y appliquant en specifiant la nature binaire des variables. Il faut ensuitelancer la resolution puis recuperer la solution trouvee et la remettre en formeselon le passage de la structure binaire a la forme d’un sudoku conventionnel.

23

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 24/37

4.2.2 Quelques resultats

Pour faciliter l’etude des resultats, le programme permet de lire une suitede sudoku dispose en ligne dans un fichier pour plus d’aisance. Les premiersresultats donnent, sur une machine commune (processeur a 1.2Ghz) :

Type de sudoku Nombres moyen (s) maxi. (s) echec(s)niveaux : ”Facile”, ”Moyen” et ”Difficile” 50 0.04 ≈ 1 0

generes aleatoirement - serie 1 1000 0.084 ≈ 1 0generes aleatoirement - serie 2  1000 0.087 ≈ 1 0generes aleatoirement - serie 3  1000 0.08 ≈ 1 0generes aleatoirement - serie 4 1000 0.08 ≈ 1 0generes aleatoirement - serie 5  1000 0.08 ≈ 1 0

niveau : ”Tres Difficile” 10000 0.303 5 0Parmi les plus difficile connus 1400 1.67 8 0

Remarque 1 : Les temps mini. ne sont pas precises car l’outil de mesure nepermet pas de les distinguer d’un temps nul.

Remarque 2 : Les niveaux de difficultes presentes sont deduit par rapporta un certains nombre de criteres directement lies a la strategie ”humaine”.Parmi lesquels on trouve : le nombre de chiffre que l’on peut placer des ledebut en utilisant des techniques d’elimination classique, le nombre de choixsuccessifs et obligatoires permettant d’arriver a la solution ou encorel’utilisation de techniques dites a reconnaissance de motifs.

Remarque 3 : Les sudoku generes aleatoirement  l’on ete grace auprogramme suexg. Il fournit un nombre de sudoku de difficultes aleatoires a

partir d’une graine lui servant pour la generation de nombres aleatoires.

On peut d’ores-et-deja remarquer que le temps de resolution est en accordavec la difficulte des sudoku proposes. Et ce, meme si la strategie employeen’est pas celle d’une logique humaine.

Enfin, ce programme ne permet pas d’enumerer toutes les solutionspossibles d’un sudoku. Mais il a la particularite de donner une reponse ausudoku vide !

1 8 4 6 5 9 2 3 72 7 6 3 8 1 9 5 4

3 9 5 4 7 2 1 8 64 1 7 2 6 3 8 9 55 2 8 9 1 7 6 4 36 3 9 5 4 8 7 1 2

7 6 1 8 3 4 5 2 98 4 2 7 9 5 3 6 19 5 3 1 2 6 4 7 8

Fig. 15 – La reponse du programme au sudoku vide

24

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 25/37

5 Conclusion

Nous pouvons rappeler la problematique : peut-on creer un algorithme deresolution de sudoku n’ayant pas a formuler de choix, donc a effectuer de testsur les solutions trouvees. La reponse s’etale, malheuresement, en deux parties.Tout d’abord, avec les techniques de programmation lineaire etalees ci-dessus,il nous est possible de considerer le sudoku comme un probleme lineaire ennombres entiers (binaires) et de le solutionner en utilisant, par exemple, lescoupes de Gomory. Celles-ci nous autorisent a chaque iteration du programmede nous rapprocher de plus en plus de la solution, jusqu’ a l’atteindre et sans”tester” des solutions fausses.

Cependant, comme il a ete note plus haut, cette methode ne permet pasune convergence rapide de l’algorithme vers la (ou en fait, une) solution. Il estcouple a une recherche arborescente, laquelle peut etre amenee a explorer des

voies non valides.Enfin, on rappellera que le probleme de la resolution d’un sudoku est

reconnu comme ayant une complexite polynomiale. On peut, bien entendue, leresoudre en testant toutes les possibilites qui sont en nombre fini.

6 Bibliographie

– Sudoku - Article Wikipedia (http://en.wikipedia.org/wiki/Sudoku)

– Programmation Lineaire - Article Wikipedia

(http://en.wikipedia.org/wiki/Linear_programming)

– Simplexe - Article Wikipedia

(http://en.wikipedia.org/wiki/Simplex_algorithm)

– "Programmation Mathematiques, Theorie d’algorithmes 2eme ed.",Michel MINOUX Tech&Doc Lavoisier, 2008

– "Introduction a l’analyse numerique matricielle et a l’optimisation"

P.G. CIARLET, 1994, Ed. Masson

– Librairie GLPK (GNU Linear Programming Kit), partie du projet GNU,

librairie sous license GPL

– Documentation de la librairie GLPK

– "Generating Gomory’s Cuts for linear integer programming problems :

the HOW and WHY" - Presentation publiee sur le site Internet de

l’Universite de Melbourne - code 620-362

– "Solving Mixed Integer Linear Programs Using Branch and Cut Algorithm",

Shon Albert, Faculty of North Carolina State University

7 Annexes

25

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 26/37

1 /********************************************************************************/2 /** SudSol **/3 /** File : MainHeader.hpp **/4 /** -> Includes and global linking **/5 /********************************************************************************/67 /********************************************************************************/8 /** Project's files **/9 /** -> MainHeader.hpp (this) **/

10 /** -> Sudoku.hpp **/11 /** -> Sudoku.cpp **/12 /** -> main.cpp **/13 /********************************************************************************/1415 #ifndef MAINHEADER_HPP_INCLUDED

16 #define MAINHEADER_HPP_INCLUDED1718 // External includes19 #include <iostream> // Input/Output20 #include <sstream> // Streams/Strings21 #include <fstream> // File IO22 #include <glpk.h> // GNU LINEAR PROGRAMMING KIT23 #include <time.h> // Clock routines2425 // Rnternal includes26 #include "./Sudoku.hpp"2728 // Colors allowed only under Unix-like platforms29 #define RESET_COLOR "\33[0;m"30 #define COL( color ) "\33[0;"##color##"m"31 #define COL_ATT( color, attribute) "\33["##attribute##";"##color##"m"3233 // Attributes34 #define ResetCol "\33[0;m"35 #define Bright "\33[1;m"36 #define Dim "\33[2;m"37 #define Underscore "\33[4;m"

38 #define Blink "\33[5;m"39 #define Reverse "\33[7;m"40 #define Hidden "\33[8;m"4142 // ForeGround Colors43 #define Black "\33[0;30m"44 #define Red "\33[0;31m"45 #define Green "\33[0;32m"46 #define Yellow "\33[0;33m"47 #define Blue "\33[0;34m"48 #define Magenta "\33[0;35m"49 #define Cyan "\33[0;36m"50 #define White "\33[0;37m"5152 // Background Colors53 #define B_Black "\33[0;40m"54 #define B_Red "\33[0;41m"55 #define B_Green "\33[0;42m"56 #define B_Yellow "\33[0;43m"57 #define B_Blue "\33[0;44m"58 #define B_Magenta "\33[0;45m"59 #define B_Cyan "\33[0;46m"

60 #define B_White "\33[0;47m"6162 /// TEMPLATE TOOLS63 template<typename TYPE>64 TYPE num_from_string(const char* str)65 {66 std::stringstream s(str);  //creating a stream67 TYPE num;68 s >> num;69 return num;70 }7172 #endif // MAINHEADER_HPP_INCLUDED73

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 27/37

1 /********************************************************************************/2 /** SudSol **/3 /** File : Sudoku.hpp **/4 /** -> Sudoku's structure definition **/5 /********************************************************************************/67 #ifndef SUDOKU_HPP_INCLUDED8 #define SUDOKU_HPP_INCLUDED9

10 //external includes11 #include <iostream>12 #include <sstream>13 #include <fstream>14 #include <glpk.h>15

16 //Constants17 const int BLOC_X[] = {0, 3, 6, 0, 3, 6, 0, 3, 6};18 const int BLOC_Y[] = {0, 0, 0, 3, 3, 3, 6, 6, 6};19 #define SSB_TRUE 220 #define SSB_FALSE 021 #define SSB_UNDEF 12223 //Objects24 struct SUDOKU25 {26 // Data27 char val[729];28 glp_prob* problem;2930 // Functions (Constructors, Destructor)31 SUDOKU(void);32 SUDOKU(const char* filename);33 ~SUDOKU(void);3435 // Basic tools36 int get_pos( int i, int j, int nbr);37 int idsquare(int i,int j);

38 int get( int i, int j);39 int getline( int j, int k);40 int getcolumn( int i, int k);41 int getblock( int b, int k);42 int put( int i, int j, int c);43 void clear(void);44 bool complete(void);4546 // IO tools47 bool load(const char* filename);48 bool load_from_string(const char* str);49 void print(const char* filename);50 void print_screen(void);51 void raw_print_screen(void);52 void valid(void);5354 // Problem tools55 void build_problem(bool verbose);56 void retrieve_information(void);57 void retrieve_information2(void);58 };59

60 #endif // SUDOKU_HPP_INCLUDED61

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 28/37

1 /********************************************************************************/2 /** SudSol **/3 /** File : Sudoku.cpp **/4 /** -> Sudoku's method code **/5 /********************************************************************************/67 #include "./Sudoku.hpp"8 #include "./MainHeader.hpp" //colors9

10 /// Functions11 // Constructor12 SUDOKU::SUDOKU(void)13 {14 clear();15 problem = NULL;

16 }1718 // Constructor : load from a file19 SUDOKU::SUDOKU(const char* filename)20 {21 clear();22 problem = NULL;23 load(filename);24 return ;25 }2627 // Destructor28 SUDOKU::~SUDOKU(void)29 {30 if(problem!=NULL)31 glp_delete_prob(problem);32 }3334 ///Basic Tools35 int SUDOKU::get_pos( int i, int j, int nbr)36 {37 ///WARNING nbr E [|1;9|]

38 return i*81+j*9+(nbr-1); //OK39 }4041 int SUDOKU::idsquare(int i,int j)42 {43 int ln = static_cast<int>( (float)(i)/3.0);44 int cl = static_cast<int>( (float)(j)/3.0);4546 return cl+3*ln;47 }4849 int SUDOKU::get( int i, int j)50 {51 ///WARNING nb E [|1;9|]52 int k;53 int m = 0;5455 for( k=1; k<=9; k++)56 {57 if( val[ get_pos(i,j,k) ]==SSB_TRUE && m!=0 )58 return -1; //ERROR59 if( val[ get_pos(i,j,k) ]==SSB_TRUE && m==0 )

60 m = k;61 if( val[ get_pos(i,j,k) ]==SSB_UNDEF )62 return 0; //UNDEFINED|UNKNOWN63 }64 if(m!=0)65 return m;66 else67 return -1;68 }6970 int SUDOKU::getline( int j, int k)71 {72 ///WARNING nb E [|1;9|]73 int i;7475 for( i=0; i<9; i++)76 {77 if( val[ get_pos(i,j,k) ]==SSB_TRUE )78 return i;79 if( val[ get_pos(i,j,k) ]==SSB_UNDEF )80 return 0; // UNDEFINED|UNKNOWN81 }

82 return -1; //error83 }8485 int SUDOKU::getcolumn( int i, int k)86 {87 ///WARNING nb E [|1;9|]88 int j;8990 for( j=0; j<9; j++)91 {92 if( val[ get_pos(i,j,k) ]==SSB_TRUE )93 return j;94 if( val[ get_pos(i,j,k) ]==SSB_UNDEF )95 return 0; // UNDEFINED|UNKNOWN96 }97 return -1; //error98 }99

100 int SUDOKU::getblock( int b, int k)101 {102 ///WARNING nb E [|1;9|]103 int _x = BLOC_X[b],

104 _y = BLOC_Y[b];105 int _i, _j;106107 for( _i=0; _i<3; _i++)108 {109 for( _j=0; _j<3; _j++)110 {111 if( val[ get_pos(_x+_i,_y+_j,k) ]==SSB_TRUE )112 return 1; // known

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 29/37

113 if( val[ get_pos(_x+_i,_y+_j,k) ]==SSB_UNDEF )114 return 0; // UNDEFINED|UNKNOWN115 }116 }117 return -1; // error118 }119120 int SUDOKU::put( int i, int j, int nb)121 {122 ///WARNING nb E [|1;9|]123 int k;124125 if(nb<1 || nb>9) std::cout << "ERROR PUT" << std::endl;126127 // erase all the elements from the case

128 for(k=1; k<=9; k++)129 val[ get_pos(i,j,k) ]=SSB_FALSE;130131 // erase all the elements from the line132 for(k=0; k<9; k++)133 val[ get_pos(i,k,nb) ]=SSB_FALSE;134135 // erase all the elements from the column136 for(k=0; k<9; k++)137 val[ get_pos(k,j,nb) ]=SSB_FALSE;138139 // erase all the elements from the block140 int x = static_cast<int>( ((float)i)/3.0 )*3;141 int y = static_cast<int>( ((float)j)/3.0 )*3;142 int i_, j_;143144 for( i_=0; i_<3; i_++)145 {146 for( j_=0; j_<3; j_++)147 val[ get_pos(x+i_,y+j_,nb) ]=SSB_FALSE;148 }149

150 // Just set the element151 val[ get_pos(i,j,nb) ]=SSB_TRUE;152153 return nb;154 }155156 void SUDOKU::clear(void)157 {158 int i;159160 for( i=0; i<729; i++)161 {162 val[i] = SSB_UNDEF;163 }164165 return ;166 }167168 bool SUDOKU::complete(void)169 {170 int i,j;171

172 for( i=0; i<9; i++)173 {174 for( j=0; j<9; j++)175 {176 char p = get(i,j);177 if( p==-1 || p==0) //Error or undef178 return false;179 }180 }181182 return true;183 }184185 ///IO Tools186 bool SUDOKU::load(const char* filename)187 {188 int i = 0,189 j = 0,190 l, length;191 std::fstream file;192 char *buffer, c;193 bool tst = true;

194195 clear();196197 file.open(filename, std::fstream::in);198199 if(!file.is_open())200 {201 std::cout << Red << " -> Unable to read the input file : " << filename << ResetCol << std::endl;202 return false;203 }204205 // get length of file:206 file.seekg (0, std::ios::end);207 length = file.tellg();208 file.seekg (0, std::ios::beg);209210 // allocate memory:211 buffer = new char [length];212213 // read data as a block:214 file.read (buffer,length);215 file.close();

216217 // read data218 for( l=0; l<length && tst; l++)219 {220 c = *(buffer+l);221 if( ('0'<=c && c<= '9') || c=='_' || c=='X' || c=='-' || c=='.')222 {223 if('0'<c && c<= '9')224 put(i, j, (c-'0'));

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 30/37

225 j++;226 if(j>=9)227 {228 j = 0;229 i++;230 if( i>=9) tst = false; // we reached the end231 }232233 }234 }235236 delete[] buffer;237238 return true;239 }

240241 bool SUDOKU::load_from_string(const char* str)242 {243 /** WARNING : length(str)>81 **/244 int i = 0,245 j = 0,246 l;247 char c;248 bool tst = true;249250 clear();251252 // read data253 for( l=0; l<81 && tst; l++)254 {255 c = *(str+l);256 if( ('0'<=c && c<= '9') || c=='_' || c=='X' || c=='-' || c=='.')257 {258 if('0'<c && c<= '9')259 put(i, j, (c-'0'));260 j++;261 if(j>=9)

262 {263 j = 0;264 i++;265 if( i>=9) tst = false; // we reached the end266 }267268 }269 }270271 return true;272 }273274 // Print on a File275 void SUDOKU::print(const char* filename)276 {277 int i, j;278 std::fstream file;279280 file.open(filename, std::fstream::out|std::fstream::ate|std::fstream::app);281282 if(!file.is_open())283 {

284 std::cout << Red << " -> Unable to create the result file" << ResetCol << std::endl;285 return ;286 }287288 for( i=0; i<9; i++)289 {290 std::cout << " ";291 for( j=0; j<9; j++)292 {293 char p = get(i,j);294 if( p==0 ) // Undef295 std::cout << Yellow << '_' << ResetCol << ' ';296 if( p==-1 ) // Error297 std::cout << Red << 'X' << ResetCol << ' ';298 if( p > 0 ) // a number299 std::cout << Black << static_cast<int>(p) << ResetCol <<' ';300 if(j==2 || j==5)301 std::cout << Blue <<  "| " << ResetCol;302 }303 std::cout << std::endl;304 if(i==2 || i==5)305 std::cout << Blue << " - - - * - - - * - - - " << ResetCol << std::endl;

306 }307308 file << std::endl;309 file << std::endl;310311 file.close();312 return ;313 }314315 // Print on SCREEN316 void SUDOKU::print_screen(void)317 {318 int i, j;319320 std::cout << Blue << " -> Printing the Sudoku : " << ResetCol << std::endl;321 for( i=0; i<9; i++)322 {323 std::cout << " ";324 for( j=0; j<9; j++)325 {326 char p = get(i,j);327 if( p==0 ) //Undef

328 std::cout << Yellow << '_' << ResetCol << ' ';329 if( p==-1 ) //Error330 std::cout << Red << 'X' << ResetCol << ' ';331 if( p > 0 ) //a number332 std::cout << Black << static_cast<int>(p) << ResetCol <<' ';333 if(j==2 || j==5)334 std::cout << Blue <<  "| " << ResetCol;335 }336 std::cout << std::endl;

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 31/37

337 if(i==2 || i==5)338 std::cout << Blue << " - - - * - - - * - - - " << ResetCol << std::endl;339 }340341 return ;342 }343344 // Layer printing345 void SUDOKU::raw_print_screen(void)346 {347 int i, j, k;348349 std::cout << Blue << " -> -RAW- Printing the Sudoku : " << ResetCol << std::endl;350 for( k=1; k<=9; k++)351 {

352 std::cout << "Layer n°" << k << std::endl;353 for( i=0; i<9; i++)354 {355 std::cout << " ";356 for( j=0; j<9; j++)357 {358 char p = val[get_pos(i, j, k)];359 std::cout << Black;360 switch(p)361 {362 case SSB_TRUE : std::cout << "1";363 break;364 case SSB_FALSE : std::cout << "0";365 break;366 case SSB_UNDEF : std::cout << "_";367 break;368 }369 std::cout << ResetCol << ' ';370 if(j==2 || j==5)371 std::cout << Blue <<  "| " << ResetCol;372 }373 std::cout << std::endl;

374 if(i==2 || i==5)375 std::cout << Blue << " - - - * - - - * - - - " << ResetCol << std::endl;376 }377 }378379 return ;380 }381382 // Printing validation383 void SUDOKU::valid(void)384 {385 //Pretty Way to verify a sudoku386 int i, j, k;387388 std::cout << " -> Verifying (TRUE,FALSE,UNDEF) : " << std::endl;389 for( i=0; i<9; i++)390 {391 for( j=0; j<9; j++)392 {393 int tmp = 0;394 std::cout << '(';395

396 for(k=1; k<=9; k++)397 if( val[ get_pos(i, j, k) ]==SSB_TRUE ) tmp++;398 std::cout << tmp << ',';399400 tmp = 0;401 for(k=1; k<=9; k++)402 if( val[ get_pos(i, j, k) ]==SSB_FALSE ) tmp++;403 std::cout << tmp << ',';404405 tmp = 0;406 for(k=1; k<=9; k++)407 if( val[ get_pos(i, j, k) ]==SSB_UNDEF ) tmp++;408 std::cout << tmp << ") ";409 }410 std::cout << std::endl;411 }412413 std::cout << std::endl;414415 return ;416 }417

418 ///Problems Tools419 void SUDOKU::build_problem(bool verbose)420 {421 /**422 Constraints423 V i,j E [|1;n|]² Sum( v[i,j, k], k, 1, n ) = 1 //only one number per case424 V k,j E [|1;n|]² Sum( v[i,j, k], k, 1, n ) = 1 //only one number per line425 V k,i E [|1;n|]² Sum( v[i,j, k], k, 1, n ) = 1 //only one number per column426 **/427428 /**429 CONSTRAINTS MATRIX430 Initialzing 3 tables of numbers431 First and Second represent coordinates of the value432 Third represent the coefficient433434 Number of coefficients : 729*4 = 2916 (each var appears 3 times)435436 Ai -> coordinate 1437 Aj -> coordinate 2438 Acoeff -> coefficient in the constraints' matrix439 **/

440441 //Methods & Macro442 #define VERBOSE if(verbose) //To provide output on screen during the processing443 //#define AFFINFO444 #define SEARCHMODE_DB //Choice between inequalities and equalities445 //#define SEARCHMODE_FX //446 #ifdef SEARCHMODE_DB447 VERBOSE std::cout << " -> Using Double Bounded Method" << std::endl;448 #else

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 32/37

449 #ifdef SEARCHMODE_FX450 VERBOSE std::cout << " -> Using Fixed Method" << std::endl;451 #else452 VERBOSE std::cout << " -> ERROR : NO SEARCHMODE DEFINED" << std::endl;453 #endif454 #endif455456 /**457 Ai -> index of constraint458 Aj -> index of var459 **/460 int Ai[2916], Aj[2916];461 double Acoeff[2916];462463 /**

464 IndGP -> get_pos -> index of var465 **/466 int IndGP[729];467468 // declare all variables469 // first we create an empty problem470 problem = glp_create_prob();471472 // then we give him a name473 glp_set_prob_name(problem, "SudoKu_Problem_Binary");474475 // set MAXIMIZE for the objective function476 glp_set_obj_dir(problem, GLP_MAX);477478 ///1 - Find the number of unknows and constraints479 int nvar = 0,480 ncns = 0;481 int IndMat = 1; // number of point in the matrix482483 { /// BUILDING BLOC484 int i, j, k, b;485

486 /// Case Constraints487 for(j=0; j<9; j++)488 {489 for(i=0; i<9; i++)490 {491 if( get(i, j)==0 ) //Case undefined492 {493 ncns++;494 ///Create the constraint495 glp_add_rows(problem, 1);496 std::stringstream s;  //creating a stream / string497 s << "C0";498 if( ncns<10 ) s << '0';499 s << ncns << "_case" ;  //set it to the name of the case500 #ifdef AFFINFO501 std::cout << " -> New Constraint : " << s.str() << std::endl;502 #endif503 glp_set_row_name(problem, ncns, (s.str()).c_str() );  // set the name of a slack variables504 #ifdef SEARCHMODE_DB505 lpx_set_row_bnds(problem, ncns, LPX_DB, 0.0, 1.0); // set the value to 0.0<=X<=1.0506 #else507 #ifdef SEARCHMODE_FX

508 lpx_set_row_bnds(problem, ncns, LPX_FX, 1.0, 1.0); // set the value to X=1.0509 #endif510 #endif511512 for(k=1; k<=9; k++) // +variables513 {514 if( val[ get_pos(i, j, k) ] == SSB_UNDEF ) //Value undefined515 {516 nvar++;517 ///create the var518 glp_add_cols(problem, 1);519 std::stringstream s;  // creating a stream / string520 s << 'X';521 if( nvar<10 ) s << '0';522 if( nvar<100 ) s << '0';523 s << nvar << '_' << i << '-' << j << '_' << k; //set it to the name of the case524 #ifdef AFFINFO525 std::cout << " -> New Unknown : " << s.str() << std::endl;526 #endif527 glp_set_col_name(problem, nvar, (s.str()).c_str()); // set the name of the variable528 glp_set_obj_coef(problem, nvar, 1.0);  // set the coefficient in the

objective function

529 glp_set_col_kind(problem, nvar, GLP_BV); /// Variables are binary530 /// Matrix update531 IndGP[get_pos(i, j, k)] = nvar;532 /// Update 2533 Ai[IndMat] = ncns;534 Aj[IndMat] = nvar;535 Acoeff[IndMat] = 1.0;536 IndMat++;537 }538 }539 }540 }541 }542543 /// Line Constraints544 for(j=0; j<9; j++)545 {546 for(k=1; k<=9; k++)547 {548 if( getline(j, k)==0 ) //line undefined549 {550 ncns++;

551 ///Create the constraint552 glp_add_rows(problem, 1);553 std::stringstream s;  //creating a stream / string554 s << "C";555 if( ncns<100 ) s << '0';556 s << ncns << "_line" ;  //set it to the name of the case557 #ifdef AFFINFO558 std::cout << " -> New Constraint : " << s.str() << std::endl;559 #endif

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 33/37

560 glp_set_row_name(problem, ncns, (s.str()).c_str() );  // set the name of a slack variables561 #ifdef SEARCHMODE_DB562 lpx_set_row_bnds(problem, ncns, LPX_DB, 0.0, 1.0); // set the value to 0.0<=X<=1.0563 #else564 #ifdef SEARCHMODE_FX565 lpx_set_row_bnds(problem, ncns, LPX_FX, 1.0, 1.0); // set the value to X=1.0566 #endif567 #endif ///A REVOIR568 ///Update569 for(i=0; i<9; i++)570 {571 if( val[ get_pos(i, j, k) ] == SSB_UNDEF ) //Value undefined572 {573 Ai[IndMat] = ncns;574 Aj[IndMat] = IndGP[ get_pos(i, j, k) ];

575 Acoeff[IndMat] = 1.0;576 IndMat++;577 }578 }579 }580 }581 }582583 /// Column Constraints584 for(i=0; i<9; i++)585 {586 for(k=1; k<=9; k++)587 {588 if( getcolumn(i, k)==0 ) //column undefined589 {590 ncns++;591 ///Create the constraint592 glp_add_rows(problem, 1);593 std::stringstream s;  //creating a stream / string594 s << "C";595 s << ncns << "_column" ;  //set it to the name of the case596 #ifdef AFFINFO

597 std::cout << " -> New Constraint : " << s.str() << std::endl;598 #endif599 glp_set_row_name(problem, ncns, (s.str()).c_str() );  // set the name of a slack variables600 #ifdef SEARCHMODE_DB601 lpx_set_row_bnds(problem, ncns, LPX_DB, 0.0, 1.0); // set the value to 0.0<=X<=1.0602 #else603 #ifdef SEARCHMODE_FX604 lpx_set_row_bnds(problem, ncns, LPX_FX, 1.0, 1.0); // set the value to X=1.0605 #endif606 #endif607 ///Update608 for(j=0; j<9; j++)609 {610 if( val[ get_pos(i, j, k) ] == SSB_UNDEF ) //Value undefined611 {612 Ai[IndMat] = ncns;613 Aj[IndMat] = IndGP[ get_pos(i, j, k) ];614 Acoeff[IndMat] = 1.0;615 IndMat++;616 }617 }618 }

619 }620 }621622 /// Bloc Constraints623 for(b=0; b<9; b++)624 {625 for(k=1; k<=9; k++)626 {627 if( getblock(b, k)==0 )628 {629 ncns++;630 ///Create the constraint631 glp_add_rows(problem, 1);632 std::stringstream s;  //creating a stream / string633 s << "C";634 s << ncns << "_block" ;  //set it to the name of the case635 #ifdef AFFINFO636 std::cout << " -> New Constraint : " << s.str() << std::endl;637 #endif638 glp_set_row_name(problem, ncns, (s.str()).c_str() );  // set the name of a slack variables639 #ifdef SEARCHMODE_DB640 lpx_set_row_bnds(problem, ncns, LPX_DB, 0.0, 1.0); // set the value to 0.0<=X<=1.0

641 #else642 #ifdef SEARCHMODE_FX643 lpx_set_row_bnds(problem, ncns, LPX_FX, 1.0, 1.0); // set the value to X=1.0644 #endif645 #endif646 ///Update647 int _i, _j;648 int _x = BLOC_X[b],649 _y = BLOC_Y[b];650 for(_i=0; _i<3; _i++)651 {652 for(_j=0; _j<3; _j++)653 {654 if( val[ get_pos(_x+_i, _y+_j, k) ] == SSB_UNDEF ) //Value undefined655 {656 Ai[IndMat] = ncns;657 Aj[IndMat] = IndGP[ get_pos(_x+_i, _y+_j, k) ];658 Acoeff[IndMat] = 1.0;659 IndMat++;660 }661 }662 }

663 }664 }665 }666667 VERBOSE std::cout << " -> There are " << nvar << " variables" << std::endl;668 VERBOSE std::cout << " -> There are " << ncns << " constraints" << std::endl;669 VERBOSE std::cout << " -> There are " << IndMat << " links in the matrix" << std::endl;670 }671

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 34/37

672 /// Load / Give to GLPK673 VERBOSE std::cout << " -> Loading Constraints Matrix" << std::endl;674 glp_load_matrix(problem, IndMat-1, Ai, Aj, Acoeff);675676 VERBOSE std::cout << " -> Problem built" << std::endl;677 return ;678 }679680 /// After solving, we must read the information returned by GLPK681 /// /!\ DEPRECATED682 void SUDOKU::retrieve_information(void)683 {684 int i,j,k, ivar = 1;685686 std::cout << " -> Retreiving information" << std::endl;

687688 //loops689 for(j=0; j<9; j++)690 {691 for(i=0; i<9; i++)692 {693 if( get(i,j)==0 ) // unknown case694 {695 std::cout << " -> At " << i << '-' << j << std::endl;696 for(k=1; k<=9; k++)697 {698 if( val[ get_pos( i, j, k) ] == SSB_UNDEF ) //is unknown699 {700 int res = static_cast<int>( lpx_get_col_prim(problem, ivar) );  // get the value found701 std::cout << " -> Val : " << k << " ~ res : " << res << std::endl;702 if( res>0 )703 val[ get_pos( i, j, k) ] = SSB_TRUE;704 else705 val[ get_pos( i, j, k) ] = SSB_FALSE;706 ivar++;707 }708

709 }710 }711 }712 }713 return ;714 }715716 /// After solving, we must read the information returned by GLPK717 void SUDOKU::retrieve_information2(void)718 {719 int i,j,k, ivar = 1;720721 #ifdef AFFINFO722 std::cout << " -> Retreiving information (Objective Function : " << glp_mip_obj_val(problem) << ")" << std::

endl;723 #endif724725 //loops726 for(j=0; j<9; j++)727 {728 for(i=0; i<9; i++)729 {

730 if( get(i,j)==0 ) // case inconnue731 {732 #ifdef AFFINFO733 std::cout << " -> At " << i << '-' << j << std::endl;734 #endif735 for(k=1; k<=9; k++)736 {737 if( val[ get_pos( i, j, k) ] == SSB_UNDEF ) //is unknown738 {739 double res = glp_mip_col_val(problem, ivar); // get the value found740 #ifdef AFFINFO741 std::cout << " -> Val : " << k << " ~ res : " << res << std::endl;742 #endif743 if( res>0.5)744 val[ get_pos( i, j, k) ] = SSB_TRUE;745 else746 val[ get_pos( i, j, k) ] = SSB_FALSE;747 ivar++;748 }749 }750 }751 }

752 }753 return ;754 }755756

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 35/37

1 /********************************************************************************/2 /** SudSol **/3 /** File : main.cpp **/4 /** -> Main funcion **/5 /********************************************************************************/67 #include "./MainHeader.hpp"89 /** Tools to process a list of Sudoku **/

10 bool process_continue(const char* filename, int beg, int l);1112 int main(int argc, char *argv[])13 {14 std::cout << "SudSol <Binary Version>" << std::endl;15

16 //Read argument given to the program17 if( argc==2 )18 {19 std::cout << " -> Loading Sudoku File : " << argv[1] << "..." << std::endl;2021 SUDOKU* s = new SUDOKU();2223 if( s->load(argv[1]) )24 {25 // Print the Sudokus loaded26 s->print_screen();2728 // Build the problem29 std::cout << " -> Building the problem..." << std::endl;30 s->build_problem(true);31 std::cout << " -> Solving the problem..." << std::endl;32 std::cout << Cyan << "[Lib GLPK] say :" << ResetCol << std::endl;33 /** SOLVING PART **/34 time_t start,end;35 double dif_s;36 time (&start);37 int code = lpx_intopt(s->problem);

38 time (&end);39 dif_s = difftime (end,start);40 /** END **/41 std::cout << Cyan << "said [Lib GLPK]" << ResetCol << std::endl;42 std::cout << " -> Solving time : " << dif_s << " second(s)" << std::endl;43 std::cout << " -> IntOpt Returned : " << std::endl;44 switch(code)45 {46 //Successes47 case LPX_E_OK:48 std::cout << Green << " OPTIMAL SOLUTION" << ResetCol << std::endl;49 break;50 case LPX_FEAS:51 std::cout << "FEASIBLE SOLUTION" << std::endl;52 break;53 //Problems :54 case LPX_INFEAS:55 std::cout << "INFEASIBLE PROBLEM" << std::endl;56 break;57 case LPX_NOFEAS:58 std::cout << "NO FEASIBLE SOLUTION" << std::endl;59 break;

60 case LPX_UNBND:61 std::cout << "UNBOUNDED SOLUTION" << std::endl;62 break;63 case LPX_UNDEF:64 std::cout << "UNDEFINED SOLUTION" << std::endl;65 break;66 default:67 std::cout << "NO CODE" << std::endl;68 break;*/69 }70 std::cout << " -> Writing solution..." << std::endl;71 // GLPK write a text file with a description of the LP72 lpx_write_cpxlp(s->problem, "SUDSOL_SEARCHERROR.lp");73 // Use the solution to build the entire Sudoku74 s->retrieve_information2();75 // Prnt it on screen76 s->print_screen();77 if( !(s->complete()) ) //Verification78 {79 std::cout << Red << " -> ERROR : Bad Solution..." << ResetCol << std::endl;80 s->raw_print_screen();81 }

82 std::cout << " -> Done." << std::endl;8384 }8586 delete s;87 }88 if( argc==1 || argc==3 )89 std::cout << Red << " -> ERROR : You must give a Sudoku (or a list and an index) to this program" << ResetCol << std::

endl;90 if( argc==4 )91 {92 std::cout << Red << "TEST" << ResetCol << "/ From : " << num_from_string<int>(argv[2]) << ", Length : " <<

num_from_string<int>(argv[3]) << std::endl;93 process_continue(argv[1], num_from_string<int>(argv[2]), num_from_string<int>(argv[3]));94 std::cout << Red << "END TEST" << ResetCol << std::endl;95 }96 if( argc>4 )97 std::cout << Red << " -> ERROR : There are too many argument [only one or two] required]" << ResetCol << std::endl;9899 std::cout << Yellow << " -> Press Enter to quit..." << ResetCol << std::endl;

100 return 0;101 }

102103 /** Another little tool **/104 bool process_continue(const char* filename, int beg, int l)105 {106 /**107 Format :108 Sudokus must begin at the first character of the line109 **/110

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 36/37

111 std::fstream file;112 time_t start,end;113 double dif_s;114 double average = 0;115 char *buffer;116 int index = 0,117 failure = 0;118119 // Load the file that contains the Sudokus120 file.open(filename, std::fstream::in);121122 if(!file.is_open())123 {124 std::cout << Red << " -> Unable to read the input file : " << filename << ResetCol << std::endl;125 return false;

126 }127128 // go to the beginning of the file129 file.seekg (0, std::ios::beg);130131 // allocate memory:132 buffer = new char [100];133134 // stop output mode of GLPK135 glp_term_out(GLP_OFF);136137 // read data as a block:138 std::cout << " -> Processing... (Beginning : " << beg << ", Length : " << l << ')' << std::endl;139  while( file.getline(buffer, 100) )140 {141 if( (index>=beg) && (index<beg+l) )142 {143 // process on the line144 SUDOKU* sud = new SUDOKU();145 if( sud->load_from_string(buffer) )146 {147 sud->build_problem(false);

148 /** SOLVING PART **/149 time (&start);150 int code = lpx_intopt(sud->problem);151 time (&end);152 dif_s = difftime (end,start);153 average += dif_s;154 /** END **/155 if( code==LPX_E_OK )156 {157 sud->retrieve_information2();158 if( sud->complete() ) // the sudokju was entirely solved159 std::cout << Blue << " -> Sudoku n°" << index << " - Optimal Solution Found [" << dif_s << "

second(s)]" ResetCol << std::endl;160 else161 {162 failure++;163 std::cout << Red << " -> Sudoku n°" << index << " - Optimal Solution Found but NOT COMPLETE [" <<

dif_s << " second(s)]" ResetCol << std::endl;164 }165 }166 else167 {

168 std::cout << Red << " -> Sudoku n°" << index << " - Optimal Solution NOT Found [Code :" << code << "] ["<< dif_s << " second(s)]" << ResetCol << std::endl;169 failure++;170 }171 }172 else173 std::cout << Yellow << "Unable to read entry n°" << index << std::endl;174 delete sud;175 }176 index++;177 }178 std::cout << " -> Done" << std::endl;179 std::cout << " -> Failures : " << failure << std::endl;180 std::cout << " -> Average solving time : " << average/static_cast<double>(l) << std::endl;181182 //restart output mode of GLPK183 glp_term_out(GLP_ON);184185 file.close();186 delete buffer;187188 return true;

189 }190

8/4/2019 Dossier Sudoku

http://slidepdf.com/reader/full/dossier-sudoku 37/37

RXWBILOHW[W([HPSOHGH5pVROXWLRQGHVXGRNXSDUPLOHVSOXVGLIILFLOHV

6XG6RO%LQDU\9HUVLRQ!7(67)URP/HQJWK!3URFHVVLQJ%HJLQQLQJ/HQJWK!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@

!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@

!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@

!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!6XGRNXQ�2SWLPDO6ROXWLRQ)RXQG>VHFRQGV@!'RQH!)DLOXUHV!$YHUDJHVROYLQJWLPH(1'7(67