29 votes

Existe-t-il une meilleure façon de deviner les variables inconnues possibles sans force brute que je fais? Apprentissage automatique?

J'ai un jeu avec les règles suivantes:

  • L'utilisateur d'une prix des fruits et a une chance d'acheter ou de vendre des articles dans leur panier de fruits à chaque tour.

  • L'utilisateur ne peut pas faire plus de 10% de changement total dans leur panier sur un tour unique.

  • Les fruits les prix changent tous les jours et lorsqu'il est multiplié par la quantité d'articles dans le panier de fruits, la valeur totale du panier de changements par rapport à la fruits changements de prix tous les jours ainsi.
  • Le programme n'en est qu'au vu du prix actuel de tous les fruits et la valeur actuelle de la basket (prix actuel de fruits * les quantités de tous les articles dans le panier).
  • Sur la base de ces 2 entrées(tous les prix des fruits et panier de la valeur totale), le programme essaie de deviner ce que les articles sont dans le panier.
  • Un panier ne peut détenir plus de 100 articles, mais des fentes peuvent être vides
  • Le joueur peut jouer plusieurs tours.

Mon but est de deviner avec précision que par le calcul économique possible (lire: pas de brute force) et de l'échelle si il y a des milliers de nouveaux fruits.

J'ai du mal à trouver une réponse, mais dans mon esprit, il n'est pas difficile. Si j'ai le tableau ci-dessous. Je pourrais le jour 1 de l'étude et d'obtenir les données suivantes:

Apple   1
Pears   2
Oranges 3

Basket Value = 217

Je peux faire un retour de serviette de calcul et d'assumer, le poids dans le panier: 0 apple, 83 poires, et 17 Oranges égale à une valeur du panier de 217.

Le lendemain, les valeurs des fruits et panier de changements. Pour (pomme = 2, de Poire De 3, 5 Oranges) avec un panier de la valeur de 348. Quand je prends mon poids supposé ci-dessus (0,83,17), je reçois une valeur totale de 334 – pas correcte! L'exécution de ce par mon script, je vois la correspondance la plus proche est 0 pommes, 76 poires, 24 oranges qui, bien que n'égale 348 lors de la variation en % de pris en compte c'est une évolution de 38% de sorte qu'il n'est pas possible!

Je sais que je peux tout à fait la force brute mais si j'ai 1000 fruits, il ne sera pas à l'échelle. Ne pas sauter sur n'importe quel train en marche, mais peut quelque chose comme un réseau neuronal rapidement écarter, peu probable, j'ai donc calculer de grands volumes de données? Je pense qu'ils ont de plus en plus évolutive/moyen plus rapide que la simple force brute? Ou est-il un autre type de solution qui pourrait obtenir le résultat?

Voici les données brutes (n'oubliez pas de programme ne pouvez voir les prix et le total de la valeur du panier uniquement):

enter image description here

Voici une partie de la force brute code (Merci @paul Hankin pour un nettoyeur exemple que le mien):

def possibilities(value, prices):
    for i in range(0, value+1, prices[0]):
        for j in range(0, value+1-i, prices[1]):
            k = value - i - j
            if k % prices[2] == 0:
                yield i//prices[0], j//prices[1], k//prices[2]

def merge_totals(last, this, r):
    ok = []
    for t in this:
        for l in last:
            f = int(sum(l) * r)
            if all(l[i] -f <= t[i] <= l[i] + f for i in range(len(l))):
                ok.append(t)
                break
    return ok

days = [
    (217, (1, 2, 3)),
    (348, (2, 3, 5)),
    (251, (1, 2, 4)),
]

ps = None
for i, d in enumerate(days):
    new_ps = list(possibilities(*d))
    if ps is None:
        ps = new_ps
    ps = merge_totals(ps, new_ps, 0.10)

    print('Day %d' % (i+1))
    for p in ps:
        print('Day %d,' % (i+1), 'apples: %s, pears: %s, oranges: %s' % p)
    print

Mise à jour - L'info jusqu'à présent est génial. Est-il judicieux de diviser le problème en deux problèmes? L'un est la génération de l'possibilités tandis que l'autre est de trouver la relation entre les possibilités(pas plus de 10% de variation quotidienne). Par décision sur les possibilités, ne pouvait pas aussi être utilisé pour aider à générer seulement les possibilités qui sont possibles, à commencer? Je ne suis pas sûr que l'approche encore mais je me sens à la fois les problèmes sont différents, mais étroitement liés. Vos pensées?

Mise à jour 2 - il y a beaucoup de questions sur le % de variation. C'est le volume total d'éléments dans le panier qui peut changer. Pour utiliser le jeu d'exemple, Imaginez que la banque dit - vous pouvez vendre/retour/acheter des fruits, mais ils ne peuvent pas être plus de 10% de votre dernière facture. Ainsi, bien que le changement dans les prix des fruits peuvent provoquer des changements dans votre panier, la valeur, l'utilisateur ne peut pas prendre toute action qui aurait un impact de plus de 10%. Donc, si la valeur était de 100, ils peuvent faire des changements qui créent l'obtenir à 110 mais pas plus.

27voto

pkpnd Points 5044

J'ai hate de vous décevoir mais je ne pense vraiment pas qu'un réseau neuronal sera utile pour ce problème, et de l'OMI, la meilleure réponse à votre question est que les conseils "ne perdez pas votre temps à essayer des réseaux de neurones".

Une simple règle de base pour décider si oui ou non les réseaux de neurones sont applicables, c'est de penser, "un adulte moyen de résoudre ce problème raisonnablement bien dans quelques secondes?" Pour des problèmes comme "ce qui est dans cette" image", "répondre à cette question", ou de "transcrire ce clip audio", la réponse est oui. Mais pour ton problème, la réponse est un plus certain pas.

Les réseaux de neurones ont des limites, et l'un, c'est qu'ils ne traitent pas très bien avec des problèmes logiques. C'est parce que les réponses sont généralement pas "lisse". Si vous prenez une image et de modifier légèrement une poignée de pixels, le contenu de l'image est toujours la même. Si vous prenez un clip audio et insérer quelques millisecondes de bruit, un réseau neuronal sera probablement encore être en mesure de comprendre ce qui est dit. Mais dans votre problème, modifiez une seule journée de "total de la valeur du panier" en 1 seul appareil, et votre réponse(s) va considérablement changer.

Il semble que la seule façon de résoudre votre problème avec un "classique" approche algorithmique. Comme indiqué, il n'y a pas d'algorithme de mieux que la force brutale, et il pourrait ne pas être possible pour écarter beaucoup. Par exemple, si chaque jour a la propriété que tous les fruits sont vendus au même prix? Le comte de chaque fruit peut varier, aussi longtemps que le nombre total de fruits est fixe, donc le nombre de possibilités est toujours exponentielle en le nombre de fruits. Si votre objectif est de produire une liste de possibilités", alors pas d'algorithme peut être mieux que de temps exponentiel depuis cette liste peut être de façon exponentielle important dans certains cas.

Il est intéressant de noter que la partie de votre problème peut être réduit à un programme linéaire en nombres entiers (ILP). Considérer une seule journée, où vous êtes donné le panier total B et de chacun des fruits du coût c_i, pour i=1 par i=n (si n le nombre total de distinct de fruits). Disons que les prix sont de taille importante, il n'est pas évident que l'on peut "remplir" le panier avec le coût unitaire de fruits. Il peut être difficile dans cette situation de même trouver une solution unique. Formulé comme un PAI, c'est équivalent à trouver les valeurs entières de x_i tels que:

sum_i (x_i*c_i) = x_1*c_1 + x_2*c_2 + ... + x_n*c_n = B

et x_i >= 0 tous 1 <= i <= n (ne peut pas avoir le négatif de fruits), et sum_i x_i <= 100 (peut avoir au plus 100 les fruits).

La bonne nouvelle est que décent ILP solveurs existe pas -- vous pouvez simplement la main sur les formules ci-dessus et le solveur va faire de son mieux pour trouver une solution unique. Vous pouvez même ajouter une "fonction objectif" que le solveur de maximiser ou de minimiser -- minimiser sum_i x_i a pour effet de minimiser le nombre total de fruits dans le panier. La mauvaise nouvelle est que le PAI est NP-complet, donc il n'y a presque aucun espoir de trouver une solution efficace pour un grand nombre de fruits (qui est égal au nombre de variables x_i).

Je pense que la meilleure approche est d'essayer de l'ILP approche, mais aussi d'introduire certains plus de contraintes sur le scénario. Par exemple, si tous les fruits avaient un autre nombre premier prix? Cela a nice bien que si vous trouvez une solution, vous pouvez énumérer un tas d'autres solutions. Si une pomme frais m et une orange frais nm et n sont premiers entre eux, alors vous pouvez "commerce" n*x pommes pour m*x oranges sans changer le panier total, pour tout entier x>0 (tant que vous avez assez de pommes et des oranges pour commencer). Si vous choisissez tous les fruits différents, le premier numéro de coûts, les coûts seront deux à deux premiers entre eux. Je pense que cette approche permettra de relativement peu de solutions pour un jour donné.

Vous pourriez aussi envisager d'autres contraintes, telles que "il ne peut pas être plus de 5 fruits d'une seule espèce dans le panier" (ajouter la contrainte x_i <= 5), ou "il ne peut y avoir plus de 5 types différents de fruits dans la corbeille" (mais c'est plus difficile à coder comme un PAI de contrainte). L'ajout de ces types de contraintes sera plus facile pour le solveur ILP pour trouver une solution.

Bien sûr, la discussion ci-dessus est concentré sur une seule journée, et que vous avez plusieurs jours de données. Si la partie la plus difficile du problème est de trouver une quelconque solution pour tous les jours et à tous (ce qui arrive si vos prix sont grandes), puis à l'aide d'un solveur ILP vous donnera un grand coup de pouce. Si des solutions sont faciles à trouver (ce qui arrive si vous avez une très-faible coût de fruits qui peut "remplir" votre panier"), et la partie la plus difficile du problème est de trouver des solutions qui sont "compatibles" sur plusieurs jours, puis de l'ILP approche pourrait ne pas être le meilleur ajustement, et, en général, ce problème semble beaucoup plus difficile à comprendre.

Edit: et comme mentionné dans les commentaires, pour certaines interprétations de la "variation de 10%" contrainte, vous pouvez même coder l'ensemble de multi-jour problème comme un PAI.

6voto

Anonymous Points 7804

Il me semble que votre approche est raisonnable, mais qu'elle dépend de la taille des numéros dans le jeu réel. Voici une implémentation complète qui est beaucoup plus efficace que la vôtre (mais qui a encore beaucoup de possibilités d'amélioration). Il conserve une liste des possibilités pour la journée précédente, puis filtre les montants de la journée en cours à 5% de la possibilité de la veille et les imprime par jour.

 def possibilities(value, prices):
    for i in range(0, value+1, prices[0]):
        for j in range(0, value+1-i, prices[1]):
            k = value - i - j
            if k % prices[2] == 0:
                yield i//prices[0], j//prices[1], k//prices[2]

def merge_totals(last, this, r):
    ok = []
    for t in this:
        for l in last:
            f = int(sum(l) * r)
            if all(l[i] -f <= t[i] <= l[i] + f for i in range(len(l))):
                ok.append(t)
                break
    return ok

days = [
    (26, (1, 2, 3)),
    (51, (2, 3, 4)),
    (61, (2, 4, 5)),
]

ps = None
for i, d in enumerate(days):
    new_ps = list(possibilities(*d))
    if ps is None:
        ps = new_ps
    ps = merge_totals(ps, new_ps, 0.05)

    print('Day %d' % (i+1))
    for p in ps:
        print('apples: %s, pears: %s, oranges: %s' % p)
    print
 

5voto

Wesam Points 768

Problème De Cadrage

Ce problème peut être décrit comme un problème d'optimisation combinatoire. Vous essayez de trouver une optimale de l'objet (une combinaison de fruits éléments) à partir d'un ensemble limité d'objets (toutes les combinaisons possibles de fruits éléments). Avec la bonne analogie et de transformations, nous pouvons réduire cette corbeille de fruits problème bien connu et largement étudié (depuis 1897), problème de sac-à-dos.

La résolution de cette classe de problèmes d'optimisation est NP-dur. Le problème de décision de répondre "Peut-on trouver une combinaison de fruits éléments avec une valeur de X?" est NP-complet. Puisque vous voulez en compte pour le pire scénario lorsque vous avez des milliers de fruits frais, votre meilleur pari est d'utiliser un metaheuristic, comme le calcul évolutionnaire.

Solution Proposée

Le calcul évolutionnaire est une famille d'inspiration biologique métaheuristiques. Ils travaillent par la révision et de mélange (en évolution) le plus candidat des solutions basées sur une fonction de remise en forme et à rejeter le moins apte à de nombreuses itérations. Le plus élevé de remise en forme d'une solution, plus il a de chance de reproduire des solutions similaires et de survivre à la génération suivante (itération). Finalement, un local ou global de la solution optimale est trouvée.

Ces méthodes fournissent un besoin de compromis lorsque l'espace de recherche est trop grand pour couvrir traditionnelle fermé la forme de la solution mathématique. En raison de la nature stochastique de ces algorithmes, les différentes exécutions des algorithmes peut conduire à différents optima locaux, et il n'y a aucune garantie que l'optimum global sera trouvé. Les chances sont bonnes que dans notre cas puisque nous avons de multiples solutions valables.

Exemple

Nous allons utiliser le Distribué des Algorithmes Évolutionnaires en Python (DEAP) cadre et de la modernisation de leur problème de sac-à-Dos exemple à notre problème. Dans le code ci-dessous, nous appliquons une forte pénalité pour les paniers avec+ de 100 articles. Cela permettra de réduire fortement leur condition physique et leur prise de la population de la piscine dans une ou deux générations. Il y a d'autres façons de traiter les contraintes qui sont également valables.

#    This file is part of DEAP.
#
#    DEAP is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as
#    published by the Free Software Foundation, either version 3 of
#    the License, or (at your option) any later version.
#
#    DEAP is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with DEAP. If not, see <http://www.gnu.org/licenses/>.

import random

import numpy as np

from deap import algorithms
from deap import base
from deap import creator
from deap import tools

IND_INIT_SIZE = 5 # Calls to `individual` function
MAX_ITEM = 100   # Max 100 fruit items in basket
NBR_ITEMS = 50   # Start with 50 items in basket
FRUIT_TYPES = 10 # Number of fruit types (apples, bananas, ...)

# Generate a dictionary of random fruit prices.
fruit_price = {i: random.randint(1, 5)  for i in range(FRUIT_TYPES)}

# Create fruit items dictionary.  The key is item ID, and the 
# value is a (weight, price) tuple.  Weight is always 1 here.
items = {}
# Create random items and store them in the items' dictionary.
for i in range(NBR_ITEMS):
    items[i] = (1, fruit_price[i])

# Create fitness function and an individual (solution candidate)
# A solution candidate in our case is a collection of fruit items.
creator.create("Fitness", base.Fitness, weights=(-1.0, 1.0))
creator.create("Individual", set, fitness=creator.Fitness)

toolbox = base.Toolbox()

# Randomly initialize the population (a set of candidate solutions)
toolbox.register("attr_item", random.randrange, NBR_ITEMS)
toolbox.register("individual", tools.initRepeat, creator.Individual, 
toolbox.attr_item, IND_INIT_SIZE)


def evalBasket(individual):
    """Evaluate the value of the basket and
    apply constraints penalty.
    """
    value = 0 # Total value of the basket
    for item in individual:
        value += items[item][1]

    # Heavily penalize baskets with 100+ items
    if len(individual) > MAX_ITEM:
        return 10000, 0
    return len(individual), value  # (items in basket, value of basket)


def cxSet(ind1, ind2):
    """Apply a crossover operation on input sets.
    The first child is the intersection of the two sets,
    the second child is the difference of the two sets.
    This is one way to evolve new candidate solutions from
    existing ones.  Think of it as parents mixing their genes
    to produce a child.
    """
    temp = set(ind1)                # Used in order to keep type
    ind1 &= ind2                    # Intersection (inplace)
    ind2 ^= temp                    # Symmetric Difference (inplace)
    return ind1, ind2


def mutSet(individual):
    """Mutation that pops or add an element.
    In nature, gene mutations help offspring express new traits
    not found in their ancestors.  That could be beneficial or 
    harmful.  Survival of the fittest at play here.
    """
    if random.random() < 0.5:  # 50% chance of mutation
        if len(individual) > 0:
            individual.remove(random.choice(sorted(tuple(individual))))
    else:
        individual.add(random.randrange(NBR_ITEMS))
    return individual,

# Register evaluation, mating, mutation and selection functions
# so the framework can use them to run the simulation.
toolbox.register("evaluate", evalKnapsack)
toolbox.register("mate", cxSet)
toolbox.register("mutate", mutSet)
toolbox.register("select", tools.selNSGA2)


def main():
    random.seed(64)
    NGEN = 50
    MU = 50
    LAMBDA = 100
    CXPB = 0.7
    MUTPB = 0.2

    pop = toolbox.population(n=MU)  # Initial population size
    hof = tools.ParetoFront()    # Using Pareto front to rank fitness

    # Keep track of population fitness stats which should 
    # improve over generations (iterations).
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", numpy.mean, axis=0)
    stats.register("std", numpy.std, axis=0)
    stats.register("min", numpy.min, axis=0)
    stats.register("max", numpy.max, axis=0)

    algorithms.eaMuPlusLambda(pop, toolbox, MU,LAMBDA,\
                              CXPB, MUTPB, NGEN, stats,\
                              halloffame=hof)
    return pop, stats, hof


if __name__ == "__main__":
    main()   

5voto

Approche De La Programmation Linéaire En Nombres Entiers

Ce met en place naturellement comme un multi-étape Entier du Programme, avec la participation dans {les pommes, les poires, les oranges} à partir de l'étape précédente en prenant en compte le calcul de la variation relative des exploitations qui doivent être limitées. Il n'y a pas de notion de optimal ici, mais nous pouvons nous tourner le "chiffre d'affaires" contrainte dans un objectif et de voir ce qui se passe.

Les solutions fournies améliorer ceux dans le graphique ci-dessus, et sont minimes dans le sens de la variation totale dans le panier holdings.

Commentaires -

  • Je ne sais pas comment vous avez calculé le "% de changement" de la colonne de votre tableau. Un changement du jour 1 au Jour 2 de 20 pommes de 21 pommes est une 4.76% changement?

  • Tous les jours, le total de vos avoirs dans les fruits est exactement 100. Il y a une contrainte que la somme des participations est <= 100. En l'absence de violation, je veux juste confirmer.

On peut définir cela comme un programme linéaire en nombres entiers, en utilisant l'entier de l'optimisation de la routine d' ortools. Je n'ai pas utilisé un solveur ILP pour une longue période de temps, et c'est un genre de pâte feuilletée, je crois (le solveur.OPTIMALE drapeau est jamais vrai, il semble, même pour les jouets des problèmes. En outre, l' ortools LP solveur ne parvient pas à trouver une solution optimale dans le cas où scipy.linprog fonctionne sans accroc)

h1,d = holdings in apples (number of apples) at end of day d
h2,d = holdings in pears at end of day d
h3,d = holdings in oranges at end of day d

Je vais vous donner deux propositions, celle qui minimise l' l1 norme de l'erreur absolue, l'autre à l' l0norme.

L' l1 solution trouve le minimum de abs(h1,(d+1) - h1,d)/h1 + ... + abs(h3,(d+1) - h3,d)/h3), en espérant que la contrainte que chaque variation relative des exploitations est inférieure à 10% si la somme de la variation relative des exploitations est réduit au minimum.

La seule chose qui l'empêche d'être un programme linéaire (en dehors de l'entier exigence) est la fonction objective non linéaire. Pas de problème, nous pouvons introduire slack variables et de faire tout son linéaire. Pour l' l1 de la formulation, 6 slack variables sont introduites, 2 par les fruits, et 6 autres contraintes d'inégalité. Pour l' l0 de la formulation, 1 mou variable est introduite, et 6 autres contraintes d'inégalité.

C'est un processus en deux étapes, par exemple, le remplacement |apples_new - apples_old|/|apples_old| avec la variable |e|, et l'ajout de contraintes d'inégalité pour assurer l'e mesure de ce que nous aimerions. Nous avons ensuite remplacer|e| avec (e+ - e-), chacune de e+, e- >0. Il peut être montré que l'une de e+, e - sera de 0, et que (e+ + e-) est la valeur absolue de l'e. De cette façon, la paire (e+, e-) peut représenter un nombre positif ou négatif. Norme des choses, mais qui ajoute un tas de variables et de contraintes. Je peux vous expliquer cela en détail un peu plus si nécessaire.

import numpy as np
from ortools.linear_solver import pywraplp


def fruit_basket_l1_ortools():

    UPPER_BOUND = 1000

    prices = [[2,3,5],
              [1,2,4],
              [1,2,3]]

    holdings = [20,43,37]

    values = [348, 251, 213]

    for day in range(len(values)):

        solver = pywraplp.Solver('ILPSolver',
                                  pywraplp.Solver.BOP_INTEGER_PROGRAMMING)
        # solver = pywraplp.Solver('ILPSolver',
        #                          pywraplp.Solver.CLP_LINEAR_PROGRAMMING)


        c = ([1,1] * 3) + [0,0,0]

        price = prices[day]
        value = values[day]

        A_eq = [[ 0,  0, 0,  0, 0,  0, price[0], price[1], price[2]]]

        b_eq = [value]

        A_ub = [[-1*holdings[0], 1*holdings[0],              0,             0,              0,              0,  1.0,    0,    0],
                [-1*holdings[0], 1*holdings[0],              0,             0,              0,              0, -1.0,    0,    0],
                [             0,             0, -1*holdings[1], 1*holdings[1],              0,              0,    0,  1.0,    0],
                [             0,             0, -1*holdings[1], 1*holdings[1],              0,              0,    0, -1.0,    0],
                [             0,             0,              0,              0, -1*holdings[2], 1*holdings[2],    0,    0,  1.0],
                [             0,             0,              0,              0, -1*holdings[2], 1*holdings[2],    0,    0, -1.0]]

        b_ub = [1*holdings[0], -1*holdings[0], 1*holdings[1], -1*holdings[1], 1*holdings[2], -1*holdings[2]]

        num_vars             = len(c)
        num_ineq_constraints = len(A_ub)
        num_eq_constraints   = len(A_eq)                

        data = [[]] * num_vars

        data[0]  = solver.IntVar(           0, UPPER_BOUND, 'e1_p')
        data[1]  = solver.IntVar(           0, UPPER_BOUND, 'e1_n')
        data[2]  = solver.IntVar(           0, UPPER_BOUND, 'e2_p')
        data[3]  = solver.IntVar(           0, UPPER_BOUND, 'e2_n')
        data[4]  = solver.IntVar(           0, UPPER_BOUND, 'e3_p')
        data[5]  = solver.IntVar(           0, UPPER_BOUND, 'e3_n')
        data[6]  = solver.IntVar(           0, UPPER_BOUND, 'x1')
        data[7]  = solver.IntVar(           0, UPPER_BOUND, 'x2')
        data[8]  = solver.IntVar(           0, UPPER_BOUND, 'x3')

        constraints = [0] * (len(A_ub) + len(b_eq))

        # Inequality constraints
        for i in range(0,num_ineq_constraints):
            constraints[i] = solver.Constraint(-solver.infinity(), b_ub[i])
            for j in range(0,num_vars):
                constraints[i].SetCoefficient(data[j], A_ub[i][j])

        # Equality constraints
        for i in range(num_ineq_constraints, num_ineq_constraints+num_eq_constraints):
            constraints[i] = solver.Constraint(b_eq[i-num_ineq_constraints], b_eq[i-num_ineq_constraints])
            for j in range(0,num_vars):
                constraints[i].SetCoefficient(data[j], A_eq[i-num_ineq_constraints][j])

        # Objective function
        objective = solver.Objective()
        for i in range(0,num_vars):
            objective.SetCoefficient(data[i], c[i])

        # Set up as minization problem
        objective.SetMinimization()

        # Solve it
        result_status = solver.Solve()

        solution_set = [data[i].solution_value() for i in range(len(data))]

        print('DAY: {}'.format(day+1))
        print('======')
        print('SOLUTION FEASIBLE: {}'.format(solver.FEASIBLE))
        print('SOLUTION OPTIMAL:  {}'.format(solver.OPTIMAL))
        print('VALUE OF BASKET:   {}'.format(np.dot(A_eq[0], solution_set)))
        print('SOLUTION   (apples,pears,oranges): {!r}'.format(solution_set[-3:]))
        print('PCT CHANGE (apples,pears,oranges): {!r}\n\n'.format([round(100*(x-y)/y,2) for x,y in zip(solution_set[-3:], holdings)]))

        # Update holdings for the next day
        holdings = solution_set[-3:]

Une seule exécution donne:

DAY: 1
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   348.0
SOLUTION   (apples,pears,oranges): [20.0, 41.0, 37.0]
PCT CHANGE (apples,pears,oranges): [0.0, -4.65, 0.0]


DAY: 2
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   251.0
SOLUTION   (apples,pears,oranges): [21.0, 41.0, 37.0]
PCT CHANGE (apples,pears,oranges): [5.0, 0.0, 0.0]


DAY: 3
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   213.0
SOLUTION   (apples,pears,oranges): [20.0, 41.0, 37.0]
PCT CHANGE (apples,pears,oranges): [-4.76, 0.0, 0.0]

L' l0 formulation est également présenté:

def fruit_basket_l0_ortools():

    UPPER_BOUND = 1000

    prices = [[2,3,5],
              [1,2,4],
              [1,2,3]]

    holdings = [20,43,37]

    values = [348, 251, 213]

    for day in range(len(values)):

        solver = pywraplp.Solver('ILPSolver',
                                  pywraplp.Solver.BOP_INTEGER_PROGRAMMING)
        # solver = pywraplp.Solver('ILPSolver',
        #                          pywraplp.Solver.CLP_LINEAR_PROGRAMMING)

        c = [1, 0, 0, 0]

        price = prices[day]
        value = values[day]

        A_eq = [[0, price[0], price[1], price[2]]]

        b_eq = [value]

        A_ub = [[-1*holdings[0],  1.0,    0,    0],
                [-1*holdings[0], -1.0,    0,    0],
                [-1*holdings[1],    0,  1.0,    0],
                [-1*holdings[1],    0, -1.0,    0],
                [-1*holdings[2],    0,    0,  1.0],
                [-1*holdings[2],    0,    0, -1.0]]


        b_ub = [holdings[0], -1*holdings[0], holdings[1], -1*holdings[1], holdings[2], -1*holdings[2]]


        num_vars             = len(c)
        num_ineq_constraints = len(A_ub)
        num_eq_constraints   = len(A_eq)                

        data = [[]] * num_vars

        data[0]  = solver.IntVar(-UPPER_BOUND, UPPER_BOUND, 'e' )
        data[1]  = solver.IntVar(           0, UPPER_BOUND, 'x1')
        data[2]  = solver.IntVar(           0, UPPER_BOUND, 'x2')
        data[3]  = solver.IntVar(           0, UPPER_BOUND, 'x3')

        constraints = [0] * (len(A_ub) + len(b_eq))

        # Inequality constraints
        for i in range(0,num_ineq_constraints):
            constraints[i] = solver.Constraint(-solver.infinity(), b_ub[i])
            for j in range(0,num_vars):
                constraints[i].SetCoefficient(data[j], A_ub[i][j])

        # Equality constraints
        for i in range(num_ineq_constraints, num_ineq_constraints+num_eq_constraints):
            constraints[i] = solver.Constraint(int(b_eq[i-num_ineq_constraints]), b_eq[i-num_ineq_constraints])
            for j in range(0,num_vars):
                constraints[i].SetCoefficient(data[j], A_eq[i-num_ineq_constraints][j])

        # Objective function
        objective = solver.Objective()
        for i in range(0,num_vars):
            objective.SetCoefficient(data[i], c[i])

        # Set up as minization problem
        objective.SetMinimization()

        # Solve it
        result_status = solver.Solve()

        solution_set = [data[i].solution_value() for i in range(len(data))]

        print('DAY: {}'.format(day+1))
        print('======')
        print('SOLUTION FEASIBLE: {}'.format(solver.FEASIBLE))
        print('SOLUTION OPTIMAL:  {}'.format(solver.OPTIMAL))
        print('VALUE OF BASKET:   {}'.format(np.dot(A_eq[0], solution_set)))
        print('SOLUTION   (apples,pears,oranges): {!r}'.format(solution_set[-3:]))
        print('PCT CHANGE (apples,pears,oranges): {!r}\n\n'.format([round(100*(x-y)/y,2) for x,y in zip(solution_set[-3:], holdings)]))

        # Update holdings for the next day
        holdings = solution_set[-3:]

Une seule exécution de cette donne

DAY: 1
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   348.0
SOLUTION   (apples,pears,oranges): [33.0, 79.0, 9.0]
PCT CHANGE (apples,pears,oranges): [65.0, 83.72, -75.68]


DAY: 2
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   251.0
SOLUTION   (apples,pears,oranges): [49.0, 83.0, 9.0]
PCT CHANGE (apples,pears,oranges): [48.48, 5.06, 0.0]


DAY: 3
======
SOLUTION FEASIBLE: 1
SOLUTION OPTIMAL:  0
VALUE OF BASKET:   213.0
SOLUTION   (apples,pears,oranges): [51.0, 63.0, 12.0]
PCT CHANGE (apples,pears,oranges): [4.08, -24.1, 33.33]

Résumé

L' l1 formulation donne plus de résultats sensées, baisse du chiffre d'affaires, beaucoup plus bas. L'optimalité vérification échoue sur toutes les courses, cependant, qui est relatif. J'ai inclus un solveur linéaire trop et qui ne le feasiblity vérifier en quelque sorte, je ne sais pas pourquoi. Le Google de fournir de précieux peu de documentation pour la ortools lib, et la plupart des il est pour le C++ lib. Mais l' l1 formulation peut être une solution à votre problème, qui peut évoluer. PAI est en général NP-complet, et est donc votre problème le plus probable.

Aussi - n'est qu'une solution existe pas sur le jour 2? Comment définissez-vous la variation en % de sorte qu'il ne dans le graphique ci-dessus? Si je savais que je pouvais procéder à une refonte du les inégalités ci-dessus et nous avons la solution générale.

5voto

greybeard Points 404

Pas une réponse, mais une tentative de faire la une de l'information à propos de ce "variation en %" peut-être que voulait dire (somme de changement dans le nombre de chaque élément calculé en arrière) plus accessible aux non-croyants dans des tas de pixel:

 | Jour 1 ! Jour 2 changer ! Jour 3 changement ! Jour 4 changement
 |$/1| # | $ !$/1| # | % | $ !$/1| # | % | $ !$/1| # | % | $
Pommes| 1 | 20 | 20 ! 2 | 21 | 4.76 | 42 ! 1 | 21 | 0 | 21 ! 1 | 22 | 4.55 | 22
Poires| 2 | 43 | 86 ! 3 | 42 | 2.38 | 126 ! 2 | 43 | 2.33 | 86 ! 2 | 43 | 0 | 86
Les Oranges| 3 | 37 | 111 ! 5 | 36 | 2.78 | 180 ! 4 | 36 | 0 | 144 ! 3 | 35 | 2.86 | 105
Total| 100 | 217 ! 100 | 9.92 | 348 ! 100 | 2.33 | 251 ! 100 | 7.40 | 213

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X