Permutations de boites

Olympiades mathématiques, énigmes et défis
Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

Re: Permutations de boites

par fatal_error » 28 Mar 2022, 08:24

Je n'ai pas l'impression qu'il s'agisse d'une variante géométrique.
Ici les contraintes sont différentes (par rapport aux problème d'avant):
On a une contrainte d'égalité et on minimise une somme

sous réserve que les poids sont tous positifs ET que les valeurs sont toutes supérieures à 1

On peut remarquer que pour deux combinaisons d'un même ensemble de boites qui ont la même valeur (produit de leur valeur)
alors il faut préférer celle qui a le plus petit poids

Le code peut être réutilisé de manière relativement similaire
Code: Tout sélectionner
const boxes = {
  'a': { id: 'a', value: 15, weight: 85 },
  'b': { id: 'b', value: 40, weight: 60 },
  'c': { id: 'c', value: 60, weight: 40 },
  'd': { id: 'd', value: 10, weight: 20 }
}
const VALUE_CONSTRAINT = 600

let candidates = new Map([[0, { value: 1, weight: 0, ids: ['identitybox'] }]])
const totalWeight = Object.values(boxes).reduce((s, x) => s + x.weight, 0)
let best = {
  value: 1,
  weight: totalWeight + 1
}
for (const box of Object.values(boxes)) {
  const next = new Map([...candidates.entries()])
  for (const candidate of candidates.values()) {
    // warn, int overflow
    const value = candidate.value * box.value
    if (value > VALUE_CONSTRAINT) {
      continue
    }

    const weight = candidate.weight + box.weight
    if (!next.has(value) || weight < next.get(value).weight) {
      const betterCandidate = { value, weight, ids: [box.id, ...candidate.ids] }
      if (weight < best.weight && value === VALUE_CONSTRAINT) {
        console.log('improve', { weight, betterCandidate })
        best = betterCandidate
      }
      next.set(value, betterCandidate)
    }
  }
  candidates = next
}

/*
improve {
  weight: 145,
  betterCandidate: { value: 600, weight: 145, ids: [ 'b', 'a', 'identitybox' ] }
}
improve {
  weight: 60,
  betterCandidate: { value: 600, weight: 60, ids: [ 'd', 'c', 'identitybox' ] }
}
*/

https://jsfiddle.net/u4d1zw2L/
la vie est une fête :)



lyceen95
Membre Complexe
Messages: 2255
Enregistré le: 15 Juin 2019, 00:42

Re: Permutations de boites

par lyceen95 » 28 Mar 2022, 09:47

Dans le code de fatal_error, on doit pouvoir largement optimiser, par ce simple changement :
Remplacer
if (value > VALUE_CONSTRAINT) {

par
if ( value > VALUE_CONSTRAINT or mod(VALUE_CONSTRAINT , value) > 0 ) {

ou en adaptant, si la fonction mod n'existe pas.

Dès que value dépasse VALUE_CONSTRAINT , ou dès que value n'est plus un diviseur de VALUE_CONSTRAINT , la piste est abandonnée.

Je pars du principe que toutes les valeurs sont des nombres entiers. Si ce n'est pas le cas, un traitement préalable de réduction au même dénominateur peut être nécessaire.

Oli1
Membre Naturel
Messages: 46
Enregistré le: 15 Jan 2021, 20:11

Re: Permutations de boites

par Oli1 » 30 Mar 2022, 17:32

Hello Fatal_Error et Lycéen95,

Merci pour ces réponses très précises,
C'est une très bonne chose que le code puisse être réutilisé d'une manière assez similaire et c'était tout le sens de ma question sur l'introduction de cette variante !
Lyceen 95 a bien raison d'aborder la problématique de l'optimisation.
A ce stade j'arrive à traiter des listes de 50M de boites. Au-delà l'algo crashe.
Pour moi dans tous les cas la solution que vous avez proposé est intéressante car l'usage des dictionnaires combiné à la mise en mémoire des combinaisons aboutit en quelques sortes à transférer une grande partie de la complexité temporelle sur de la complexité spatiale (la mémoire).
Du coup il me semble que l'optimisation de ce code se joue principalement sur la gestion de la mémoire et celle de la structure de donnée des dictionnaire qui permettent de traiter les combinaisons des candidates.

Bien à vous,

Olivier

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

Re: Permutations de boites

par fatal_error » 01 Avr 2022, 07:26

Hi Oli1

oui c'est le compromis complexité temporelle/spatiale
on peut toujours essayer de compacter la donnée pour la stocker mais ça ne fait que retarder l'échéance (les temps d'accès on peut généralement s'en sortir en o(1) avec hashmap)

maintenant le mot clé qu'il faut conserver c'est élagage idem élimination au plus tot des candidats qui ne mèneront à rien (ou pas mieux).

Par exemple le modulo de Lycéen95 qui permet de réduire grandement les candidats
la vie est une fête :)

Oli1
Membre Naturel
Messages: 46
Enregistré le: 15 Jan 2021, 20:11

Re: Permutations de boites

par Oli1 » 12 Avr 2022, 10:36

Hello Fatal_Error,

En faisant tourner votre code (avec une approche Map et avec une approche Object pour la structuration de la base de données), on observe toujours le même phénomène troublant : pour un même nombre d'items à l'entrée du problème, le nombre d'itérations dans le dictionnaire et le temps de traitement varient de façon aléatoire et parfois très importante. Ce que je veux dire c'est que pour un même nombre d'items, le temps de traitement peut varier parfois de *1 à *100 et le nombre d'itérations de *1 à *1000 ce qui est énorme.

Exemple : pour une série de 100 tests à 100000 items :

result: {
functionName: 'Item_ordering_test',
itemCount: 100000,
testCount: 100,
memory: {
rss: { min: '69.24 MB', max: '180.73 MB', avg: '133.59 MB' },
heapTotal: { min: '55.79 MB', max: '149.32 MB', avg: '101 MB' },
heapUsed: { min: '21.33 MB', max: '125.11 MB', avg: '69.81 MB' },
external: { min: '0.21 MB', max: '0.23 MB', avg: '0.23 MB' },
arrayBuffers: { min: '0.02 MB', max: '0.03 MB', avg: '0.02 MB' }
},
time: {
min: { time: '00:00:00,025', candidateIterationCount: '699,974' },
max: { time: '00:00:01,505', candidateIterationCount: '99,841,395' },
avg: '00:00:00,702'
}
}

On s'aperçoit en faisant d'autres tests que :
- Le fait de régler le générateur d'items pour avoir 100% d'items exploitables n'y change rien.
- L'élimination des items à l'entrée qui ne servent à rien n'a qu'un impact très marginal sur le temps
- Le pré-ordonnancement des listes de candidats à l'entrée (par exemple dans un ordre décroissant) n'a aucun effet sur le nombre d'itérations et donc n'a aucun effet sur les temps de traitement.
- L'ordre d'écriture des candidats dans le dictionnaire (inscription du nouveau candidat en dessous ou au dessus des candidats précédents) n'a aucun effet sur le nombre d'itérations du dictionnaire.

Il y a donc dans le fonctionnement de ce code une variable aléatoire qui engendre un nombre plus ou moins important d'itérations et fait en sorte que le dictionnaire fera beaucoup d'écrasements, auquel cas le traitement est rapide, ou peu d'écrasements auquel cas le traitement est lent.

Sauriez-vous me dire en quoi consiste ce facteur aléatoire et s'il y aurait un moyen de l'optimiser ? Car elle se trouve incontestablement ici la source principale d'optimisation de ce code.

En vous remerciant pour vos lumières,

lyceen95
Membre Complexe
Messages: 2255
Enregistré le: 15 Juin 2019, 00:42

Re: Permutations de boites

par lyceen95 » 12 Avr 2022, 16:04

Tu demandes comment optimiser un code...
Mais on a un peu de mal à remonter la discussion pour trouver la version actuelle de ton code.
Et on ne sait pas trop quel est l'énoncé du truc que tu cherches à optimiser.
Donc abandon.

Oli1
Membre Naturel
Messages: 46
Enregistré le: 15 Jan 2021, 20:11

Re: Permutations de boites

par Oli1 » 12 Avr 2022, 17:29

Voilà tout simplement :
https://jsfiddle.net/g2z5y9kd/

lyceen95
Membre Complexe
Messages: 2255
Enregistré le: 15 Juin 2019, 00:42

Re: Permutations de boites

par lyceen95 » 12 Avr 2022, 23:59

Ca c'est ton code.
Et je dois donc deviner l'objectif.
On a un certain nombre de boites (25 ici). Chaque boite a une Value et un Weight.
On doit trouver la combinaison de boites qui va donner une Somme des Value la plus grande possible, sans que la somme des Weight dépasse un certain total (934 ici)
L'histoire où on multipliait des nombres entiers... ça a disparu.

Ok, très classique, mais toujours intéressant.

Pour chaque boite, tu peux calculer une 'densité' : Densité = Value/Weight.
Par exemple, la boite 1 a une densité 587/16, ça semble être la valeur la plus élevée.
La dernière a une densité 7/901, très très faible.
Visiblement, la solution finale sera de prendre des boites avec une forte densité, et d'éviter les boites avec une faible densité.
Tu tries les boites de la plus dense à la moins dense.
Et tu fais tourner ton programme, en changeant très peu de choses.
A chaque fois que tu trouves une solution qui est mieux que les solutions déjà trouvées, tu mémorises cette solution, avec la somme des Weight correspondante (BestWeight) et la somme des Values (BestValue)
Quand tu explores les solutions suivantes, quand tu as une situation avec une certaine Somme des Weight SW et une certaine somme des valeurs SV, et la densité de la dernière boite analysée Dens,
Tu es sûr que , même si les boites restantes se combinent à merveille, la somme des Value que tu pourras atteindre ne pourra pas dépasser SV+ Dens*(934-SW)
Si ce nombre est inférieur à BestValue, alors la piste en question est une impasse, tu ne pourras pas améliorer ton score.
Tu peux alors élaguer ton arbre, inutile de parcourir toutes cette branche jusqu'au bout.

Tu peux aussi avoir un autre tableau pour élaguer plus vite. Comme on a trié les boites en fonction de la densité, les boites vers la fin sont plutôt des grosses boites. Et tu peux créer un tableau pour dire : au-dela de la boite n, toutes les boites ont un Weight supérieur à xxx.
Et du coup, si à un moment tu as un scénario pour lequel la somme des Weight vaut 900 par exemple, mais que tu sais qu'il n'y a aucune boite de moins de 34 dans la suite, pas la peine de regarder les 20 boites restantes une par une. Ca fait un peu double emploi avec les tests sur la densité. Le bénéfice devrait être moindre.

Selon les données à traiter, tu peux diviser tes temps de traitements par 10, 100 ou même 1000.
Ici, c'est clair que la solution optimale va être constituée par quelques boites à choisir parmi les 12 ou 15 qui ont la plus forte densité. Le traitement devrait très vite couper toutes les branches inutiles.

L'autre piste, c'est que ce genre de besoin est très courant. Je suis convaincu qu'il existe des 'build-in' toutes faites, qui te donnent la solution en une ligne de commande.

 

Retourner vers ⚔ Défis et énigmes

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 5 invités

cron

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite