logigrammen oplossen


Download het programma door op het bliksem icoon hierboven te klikken.
Hieronder staat de beschrijving met de Delphi source code.

Alle mogelijke combinaties worden systematisch doorlopen tot de juiste is gevonden.
Zo'n aanpak heet de "brute-kracht" methode.

unit Unit1;
{
--------------------------------
  logigram:
---------------------------------------------------------------------------
5 personen: Mary, Wim, Peter, Anne en Corry,
5 opdrachten: huiswerk, examen, proefwerk, tentamen en overhoring
5 vakken: taal, rekenen, aardrijkskunde, geschiedenis en biologie
5 cijfers: 3,5,6,7 en 9
De voorwaarden zijn:
1) De persoon die het tentamen maakte, kreeg een hoger cijfer
   dan degene die moest rekenen, maar een lager cijfer dan Corry.
2) Peter is niet degene die huiswerk moest maken. Hij heeft echter
   een hoger cijfer dan Anne, maar een lager cijfer dan degene die
   geschiedenis deed. We weten ook dat Peter geen 4 en ook geen 6 punten
   meer kreeg dan de persoon die examen deed.
3) De leerlinge die het proefwerk maakte, kreeg een hoger cijfer
   dan Mary die geen taal deed, maar een lager cijfer dan degene
   die biologie deed.
4) Wim deed aardrijkskunde en kreeg een hoger cijfer dan degene
   die taal deed. Hij kreeg echter een lager cijfer dan de persoon
   die een overhoring had.
----------------------------------------------------------------------------
Welke opdracht, vak, cijfer hoort bij wie?

Dit programma berekent oplossingen.

Uitgangspunt is deze tabel:

--------------------------------------------------------------
                             kolom                            |
--------------------------------------------------------------
    0           1             2                3              |
persoon |   opdracht  |      vak        |   cijfer  |     rij |
--------------------------------------------------------------
mary      huiswerk       taal                 3     |      0  |
wim       examen         rekenen              5     |      1  |
peter     proefwerk      aardrijkskunde       6     |      2  |
anne      tentamen       geschiedenis         7     |      3  |
corrie    overhoring     biologie             9     |      4  |
--------------------------------------------------------------

Bij het zoekem maar oplossingen blijven de personen in kolom 0
op hun plaats staan.
De rijen in kolommen 1 t/m 3 verwisselen echter, tot een combinatie
is gevonden zonder conflicten.

De opdrachten, vakken, personen en cijfers worden in het programma
aangeduid met hun rij in de tabel hierboven, dus als 0,1,2,3 of 4.
Omdat de personen niet van plaats wisselen, vormen die tevens het
nummer van een rij.

Per kolom zijn er 5! = 120 volgorden van de opdrachten,vakken of cijfers.
Een volgorde wordt met een getal van 0..119 aangegeven.
Er zijn dus 3 van die getallen nodig, ze staan in array permNr[1..3].
(permNr staat voor permutatie nummer)
PermNr[2] geeft de permutatie aan van kolom 2.

In de tabel hierboven is van elke kolom permutatie 0 weergegeven.

De getallen permutNr[1], permutNr[2] en permutNr[3] vormen een telwerk.
Elke teller telt van 0..119, bij 120 gaat de teller over de kop naar 0
en verhoogt de volgende teller.
PermutNr[1] van 120 naar 0 verhoogt PermuNr[2], enzovoort.
Zo worden alle tellerstanden systematisch doorlopen.

Bij elke stand 0..119 hoort een volgorde van de elementen
in een kolom.

De omzetting van tellerstand naar permutatie (volgorde) gaat zo:
(stel dat we permutatie nummer 88 willen berekenen)

   maak eerst permutatie 0 , dat is 0 1 2 3 4
   deel 88 door 24 = 3  (88 div 24 = 3)    opm. 24 = 4!
   pak het 3e element uit de rij, dat is de -3-
   de rest bij deling van 88 door 24 = 16   (88 mod 24 = 16)
   haal de 3 weg uit de rij, dat levert de nieuwe rij 0 1 2 4 x
   deel 16 door  6 , 16 div 6 = 2
   neem het 2e element uit de rij, dat is de -2-
   16 mod 6 = 4   opm. 6 = 3!
   haal de 2 weg uit de rij, dat levert 0 1 4 x x
   4 div 2 = 2
   neem 2e getal uit de rij, dat is de -4-
   4 mod 2 = 0
   haal de 4 weg uit de rij, dat levert 0 1 x x x
   0 div 1 = 0
   neem 0e getal uit de rij, dat is de -0-
   de nieuwe rij wordt 1 x x x x
   pak nu het overgebleven getal, de -1-
   De volgorde nummer 88 is dus 3 2 4 0 1

Wordt deze volgorde toegepast op de kolom "opdracht" dan wordt die kolom:

   tentamen
   examen
   overhoring
   huiswerk
   examen

Maar de volgorde kan natuurlijk ook op andere kolommen slaan.

Dat achtereenvolgens wordt gedeeld door 24, 6, 3 en 1 zit zo:
er zijn 24 permutaties van 4 elementen. Deze kunnen als modulo 24
teller worden beschouwd.
Bij de eerste 24 permutaties zal de -0- voorop staan.
Bij permutaties 24..47 zal de 1 voorop staan. Enzovoort.

getal div 24 bepaalt dus het meest linkse element.
We verwijderen dit cijfer uit de rij 0 1 2 3 4, want het mag niet
opnieuw worden gekozen.
getal mod 24 is de rest bij deling en met dit getal gaan we verder.
Er zijn nu nog 4 cijfers te bepalen.
De laagste 3 vormen een modulo 3! = 6 teller zodat het hoogste cijfer
wordt bepaald door rest div 6.
En rest = rest mod 6 is wat overblijft.
Elk gevonden getal verwijderen we uit de rij.
Tenslotte wordt het laatste, overgebleven, getal aan de volgorde toegevoegd.

Om niet steeds dezelfde permutaties te hoeven berekenen wordt de tabel
permuts[0..4,0..119] bij het starten van het programma gevuld.

}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, Grids;

{door Delphi gedefinieerde typen en variabelen}

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    StaticText1: TStaticText;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{door programmeur ingevoerde typen en variabelen}

type Tstring5 =  array[0..4] of string;
     Tactivity = record
                  rij : byte;           //plaats in de tabel hierboven
                  kolom : byte;
                 end;

const Spersonen   : Tstring5 =          //inhoud van de velden in tabel
                   ('mary','wim','peter','anne','corrie');
      Svakken     : Tstring5 =
                   ('taal','rekenen','aardrijkskunde','geschiedenis','biologie');
      Sopdrachten : Tstring5 =
                   ('huiswerk','examen','proefwerk','tentamen','overhoring');
      Scijfers    : Tstring5 =
                   ('3','5','6','7','9');

      huiswerk      : Tactivity = (rij:0; kolom:1); //plaats in de tabel
      examen        : Tactivity = (rij:1; kolom:1);
      proefwerk     : Tactivity = (rij:2; kolom:1);
      tentamen      : Tactivity = (rij:3; kolom:1);
      overhoring    : Tactivity = (rij:4; kolom:1);
      taal          : Tactivity = (rij:0; kolom:2);
      rekenen       : Tactivity = (rij:1; kolom:2);
      aardrijkskunde: Tactivity = (rij:2; kolom:2);
      geschiedenis  : Tactivity = (rij:3; kolom:2);
      biologie      : Tactivity = (rij:4; kolom:2);

      mary = 0;     //plaats in de kolom
      wim = 1;
      peter = 2;
      anne = 3;
      corrie = 4;

var permutNr : array[1..3] of byte; //hold permutation-index of columns
    permuts  : array[0..4,0..119] of byte;

    getal : byte = 0;   //alleen voor testen van permutaties
    opl   : byte;       //nummer van de oplossing

{oplossing
 persoon - vak - opdracht - cijfer .....vormen een telwerk
 verhoog steeds het telwerk (= volgende permutatie) en test voor conflicten
}

procedure makepermuts;
//maak alle permutaties in array permuts[0..4,0..119]
const delers : array[0..3] of byte = (24,6,2,1);
var i,j,k,rest,quot : byte;
    pel : array[0..4] of byte; //pel : permutatie van elementen 01234
begin
 for j := 0 to 119 do
  begin
   for i := 0 to 4 do pel[i] := i;//set permutatie 0 = 01234
   rest := j;
   for i := 0 to 3 do
    begin                         //maak permutatie j
     quot := rest div delers[i];
     rest:= rest mod delers[i];
     permuts[i,j] := pel[quot];
     for k := quot to 3 do pel[k] := pel[k+1]; //shift elements down
     pel[4] := 0;
    end;
   permuts[4,j] := pel[0];//overblijvende getal
  end;//for j
end;

procedure setCells;
//vul de tabel volgens permtNr[1..3]
var i,j,k : byte;
begin
 with form1.stringgrid1 do
  for i := 0 to 3 do        //kolommen
   begin
     if i <> 0 then k := permutNr[i];
     for j := 0 to 4 do     //rijen
     case i of
      0 : cells[i,j] := Spersonen[j];
      1 : cells[i,j] := Sopdrachten[permuts[j,k]];
      2 : cells[i,j] := Svakken[permuts[j,k]];
      3 : cells[i,j] := Scijfers[permuts[j,k]];
     end;//case
    end;
end;

procedure setBeginstand;
var i : byte;
begin
 for i := 1 to 3 do permutNr[i] := 0;
 setCells;
 form1.statictext1.caption := 'beginstand';
 opl := 0;
end;

function cijfer(n : byte) : byte;
//n: 0..4
//bepaal cijfer in rij n
var k : byte;
begin
 k := permuts[n,permutNr[3]];
 result := strtoint(Scijfers[k]);
end;

function persoon(a : Tactivity) : byte;
//bepaal persoon van activiteit a
var i,kolom,rij,k : byte;
begin
 kolom := a.kolom;
 rij := a.rij;

//--zoek rij nr in kolom

 k := permutNr[kolom];
 for i := 0 to 4 do
  if permuts[i,k] = rij then result := i
end;

function checkOK : boolean;
//test of aan voorwaarden is voldaan
begin
 result := true;

//voorwaarde 1a

 if cijfer(persoon(tentamen)) <= cijfer(persoon(rekenen)) then
  begin
   result := false; exit;
  end;

//voorwaarde 1b

  if cijfer(persoon(tentamen)) >= cijfer(corrie) then
   begin
    result := false; exit;
   end;

//voorwaarde 2a

  if persoon(huiswerk) = peter then
   begin
    result := false; exit;
   end;

//voorwaarde 2b

  if cijfer(peter) <= cijfer(anne) then
   begin
    result := false; exit;
   end;

//voorwaarde 2b

 if cijfer(peter) >= cijfer(persoon(geschiedenis)) then
  begin
   result := false; exit;
  end;

//voorwaarde 2c

 if cijfer(peter) = cijfer(persoon(examen)) + 6 then
  begin
   result := false; exit;
  end;

//voorwaarde 2d

 if cijfer(peter) = cijfer(persoon(examen)) + 4 then
  begin
   result := false; exit;
  end;

//voorwaarde 3a

 if cijfer(persoon(proefwerk)) <= cijfer(mary) then
  begin
   result := false; exit;
  end;

//voorwaarde 3b

 if persoon(taal) = mary then
  begin
   result := false; exit;
  end;

//voorwaarde 3c

 if cijfer(persoon(proefwerk)) >= cijfer(persoon(biologie)) then
  begin
   result := false; exit;
  end;

//voorwaarde 3d

 if (persoon(proefwerk) = wim) or (persoon(proefwerk) = peter) then
  begin
   result := false; exit;
  end;
  
//voorwaarde 4a

 if wim <> persoon(aardrijkskunde) then
  begin
   result := false; exit;
  end;

//voorwaarde 4b

 if cijfer(wim) <= cijfer(persoon(taal)) then
  begin
   result := false; exit;
  end;

//voorwaarde 4c

 if cijfer(wim) >= cijfer(persoon(overhoring)) then
  begin
   result := false; exit;
  end;
//---------------
end;

procedure TForm1.FormCreate(Sender: TObject);
//delphi roept deze procedure aan bij opstarten
begin
 makepermuts;
 SetBeginstand;
end;

procedure TForm1.Button1Click(Sender: TObject);
//debugging only
begin
 getal := 0;
 repeat
  showmessage(inttostr(permuts[0,getal])+inttostr(permuts[1,getal])+
              inttostr(permuts[2,getal])+inttostr(permuts[3,getal])+
              inttostr(permuts[4,getal]));
  inc(getal);
 until getal = 120;
end;

function Increment : boolean;
//verhoog de teller permutNt[1..3]
//carry = overdracht, verhogen volgende positie
var carry : boolean;
    i : byte;
begin
  carry := true;
  for i := 1 to 3 do
   begin
    if carry then inc(permutNr[i]);
    if permutNr[i] = 120 then permutNr[i] := 0
    else carry := false;
   end;//for
 result := carry;
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
//aanroep bij klikken op knop "oplossen"
//zoeken naar oplossingen
var i : byte;
    carry : boolean;
begin;
 statictext1.caption := 'computer zoekt...';
 carry := false;
 repeat
  if checkOK then
   begin
    inc(opl);
    statictext1.caption := 'oplossing gevonden'+' : '+inttostr(opl);
    setCells;
    increment;
    exit;
   end;
 until increment;//until overflow
 statictext1.caption := 'einde';
 setbeginstand;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
//aanroep bij klokken op knop "beginstand" 
begin
 setbeginstand;
end;

end.