Problème de rafting (variante de sac à dos)

20

Premier puzzle de ma part, des suggestions d'amélioration reçues avec plaisir!

Le scénario est; Vous travaillez en tant que manager pour une entreprise de rafting en eau vive. Chaque matin, une liste de réservations vous est remise et vous devez les trier en radeaux. Écrivez un programme ou une fonction dans la langue de votre choix qui le fait pour vous.

Chaque radeau peut accueillir un maximum de nclients, et chaque réservation est pour un groupe entre 1 et npersonnes (inclus). Les règles suivantes doivent être respectées;

  • Aucun groupe ne peut être divisé. S'ils ont réservé ensemble, ils doivent tous être dans le même radeau.

  • Le nombre de radeaux doit être minimisé.

  • Sous réserve des deux règles précédentes, les groupes doivent être répartis le plus équitablement possible entre les radeaux.

Contributions. Le nombre n(vous pouvez supposer qu'il s'agit d'un entier positif) et la taille de toutes les réservations. Il peut s'agir d'un tableau, d'une liste ou d'une structure de données similaire si votre langue prend en charge de telles choses. Tous ces éléments seront des entiers positifs compris entre 1 etn . L'ordre des réservations n'est pas défini, ni important.

Production. Une liste des numéros de réservation, regroupés en radeaux. Le groupement doit être indiqué sans ambiguïté, tel que;

  • une liste ou un tableau de tableaux.
  • une liste séparée par des virgules pour chaque radeau. Retour à la ligne entre chaque radeau.

La façon dont vous implémentez la troisième règle dépend de vous, mais cela pourrait impliquer de trouver l'occupation moyenne du radeau et de minimiser autant que possible les écarts par rapport à celle-ci. Voici quelques cas de test.

n  Bookings       Output
6  [2,5]          [5],[2]
4  [1,1,1,1,1]    [1,1,1],[1,1]
6  [2,3,2]        [2,2],[3]
6  [2,3,2,3]      [2,3],[2,3]
6  [2,3,2,3,2]    [2,2,2],[3,3]
12 [10,8,6,4,2]   [10],[8,2],[6,4]
6  [4,4,4]        [4],[4],[4]
12 [12,7,6,6]     [12],[7],[6,6]

Les règles standard s'appliquent, le code le plus court l'emporte. S'amuser!

Édité; Une façon suggérée de définir le plus équitablement possible la troisième règle.

Une fois le nombre de radeaux rdéterminé (sous réserve de la deuxième règle), l'occupation moyenne apeut être calculée en additionnant les réservations et en divisant par r. Pour chaque radeau, l'écart par rapport à l'occupation moyenne peut être trouvé en utilisant d(x) = abs(n(x)-a), où n(x)est le nombre de personnes dans chaque radeau et 1 <= x <= r. Pour une fonction continue, à valeur unique f(y), qui est strictement positive et qui a une première dérivée strictement positive et une seconde dérivée non négative pour tout positif y, nous définissons une quantité non négative F, comme la somme de tous les f(d(x)), 1 <= x <= r. Tout choix d'allocation de radeaux qui satisfait aux deux premières règles et où Fest égal au minimum global satisfera également la troisième règle.

Gwyn
la source
3
Pour référence future, vous pouvez publier dans notre bac à sable pour obtenir des commentaires sur un défi avant de publier.
Wheat Wizard
Bienvenue sur Programmation Puzzles & Code Golf! Cela ressemble à un beau défi, sachant que c'est votre premier défi. La prochaine fois, cependant, il pourrait être préférable de publier le défi dans le bac à sable en premier, afin que les gens puissent y faire des suggestions. Ensuite, lorsque vous pensez que le défi est terminé, vous pouvez le publier sur le site principal. Merci d'avoir lu et bonne journée!
Matthew Roh
Comment est-il mesuré le plus également possible ?
Dennis
@Dennis; Je mettrai une manière suggérée de définir ceci dans une édition. Cependant, si vous avez une méthode différente et que vous pouvez la justifier pour votre réponse, c'est très bien.
Gwyn
1
Laisser les choses à la mise en œuvre est très bien, tant qu'il est clair ce qui est valide et ce qui ne l'est pas, et votre dernière modification atteint cet imo. Je suis un peu surpris que nous ne puissions pas utiliser g(y) = y(deuxième dérivé zéro) ou g(y) = y²(premier dervier zéro quand y = 0).
Dennis

Réponses:

2

Perl 6 , 163 158 octets

{[grep $^n>=*.all.sum,map ->\p{|map {p[0,|$_ Z..^|$_,p]},(1..^p).combinations},$^s.permutations].&{.grep: .map(+*).min}.min({.map((*.sum-$s.sum/$_)**2).sum})}

Essayez-le en ligne!

Comment ça fonctionne

  • map ->\p{|map {p[0,|$_ Z..^|$_,p]},(1..^p).combinations},$^s.permutations

    Génère toutes les partitions possibles de toutes les permutations du tableau d'entrée.

  • grep $^n>=*.all.sum,

    Filtre ceux où aucun radeau n'est surbooké.

  • .&{.grep: .map(+*).min}

    Filtre ceux où le nombre de radeaux est minimal.

  • .min({.map((*.sum-$s.sum/$_)**2).sum})}

    Obtient le premier avec un minimum ∑ (n x -a) 2 .

-4 octets grâce à @ Pietu1998

smls
la source
Devez-vous faire .abssi vous conciliez le résultat?
PurkkaKoodari
@ Pietu1998: Je ne sais pas, bonne prise.
smls
3

Haskell 226 228 234 268 octets

Réponse naïve à Haskell

import Data.List
o=map
u=sum
p=foldr(\x t->o([x]:)t++[(x:y):r|(y:r)<-t>>=permutations])[[]]
m x=foldl(\[m,n]x->[m+(x-m)/(n+1),n+1])[0,0]x!!0
a!z=abs$u z-a
s t=(length t,u$o((m$o u t)!)t)
a n=head.sortOn s.filter(all$(<=n).u).p

Ou non golfé

partition' :: [a] -> [[[a]]]
partition' [] = [[]]
partition' (x:xs) = [[x]:ps     | ps <- partition' xs]
                 ++ [(x:p):rest | ps <- partition' xs, (p:rest) <- permutations ps]

-- from Data.Statistics
mean :: [Double] -> Double
mean xs = fst $ foldl (\(m, n) x -> (m+(x-m)/n+1, n+1)) (0, 0) xs

diff :: Double -> [Double] -> Double
diff avg xs = abs $ sum xs - avg

rawScore :: [[Double]] -> Double
rawScore xs = sum . map (diff avg) $ xs where avg = mean . map sum $ xs

score :: [[Double]] -> (Int, Double)
score xs = (length xs, rawScore xs)

-- from Data.Ord
comparing :: (Ord b) => (a -> b) -> a -> a -> Ordering
comparing p x y = compare (p x) (p y)

candidates :: Double -> [Double] -> [[[Double]]]
candidates n xs = filter (all (\ ys -> sum ys <= n)) . partition' $ xs

answer :: Double -> [Double] -> [[Double]]
answer n xs = minimumBy (comparing score) $ candidates n xs

Avec quelques cas de test

import Text.PrettyPrint.Boxes

testCases :: [(Double, [Double])]
testCases = [(6 , [2,5])
            ,(4 , [1,1,1,1,1])
            ,(6 , [2,3,2])
            ,(6 , [2,3,2,3])
            ,(6 , [2,3,2,3,2])
            ,(12, [10,8,6,4,2])
            ,(6 , [4,4,4])
            ,(12, [12,7,6,6])]

runTests tests = transpose 
                 $ ["n", "Bookings", "Output"]
                 : map (\(n, t) -> [ show . floor $ n
                                   , show . map floor $ t
                                   , show . map (map floor) $ a n t]) tests

test = printBox 
     . hsep 3 left . map (vcat top) . map (map text) . runTests $ testCases

test rendements

n    Bookings       Output
6    [2,5]          [[2],[5]]
4    [1,1,1,1]      [[1,1],[1,1,1]]
6    [2,3,2]        [[2,2],[3]]
6    [2,3,2,3]      [[2,3],[2,3]]
6    [2,3,2,3,2]    [[2,2,2],[3,3]]
12   [10,8,6,4,2]   [[10],[8,2],[6,4]]
6    [4,4,4]        [[4],[4],[4]]
12   [12,7,6,6]     [[12],[7],[6,6]]

Éditer

Merci à @flawr et @nimi pour leurs conseils.

Écrasé p un peu.

Rasé quelques octets.

walpen
la source
1
Vous pouvez définir s=sumpuis utiliser à la splace de sum, et peut-être vous pouvez également remplacer fst$ ...par ...!!0.
flawr
1
Vous pouvez remplacer minimumBy(c s)avec head.sortOn set supprimer la fonction c. Aussi: \t->sum t<=nest (<=n).sum.
nimi
@flawr, bonne suggestion, merci!
walpen
0

Python3, 224 octets

def p(c):
 if len(c)==1:yield[c];return
 for s in p(c[1:]):
  for n,u in enumerate(s):yield s[:n]+[[c[0]]+u]+s[n+1:]
  yield[[c[0]]]+s
s=sum
r=lambda n,b:min(p(b),key=lambda c:s(abs(s(x)-s(b)/(s(b)//n+1))for x in c))

Avec des testcases:

tc = [[6,[2,5]],[4,[1,1,1,1,1]],[6,[2,3,2]],[6,[2,3,2,3]],[6,[2,3,2,3,2]],[12,[10,8,6,4,2]],[6,[4,4,4]],[12,[12,7,6,6]]]
for case in tc:
    print(str(case[0]).ljust(3),str(case[1]).ljust(16),"|",r(*case))

Comment ça marche?

La pfonction génère simplement toutes les partitions d'une liste donnée (toutes les façons possibles de la diviser en sous-listes). s=sumrenomme simplement la fonction somme, donc la dernière ligne fait tout le travail.

r=lambda n,b:min(p(b),key=lambda c:s(abs(s(x)-s(b)/(s(b)//n+1))for x in c))
r=lambda n,b:                                                               Initialize the lambda
                 p(b)                                                       Calculate all possible raft arrangements
                     ,key=lambda c:                                         Map the following lambda onto the list:
                                              s(b)/(s(b)//n+1)              Calculate the ideal average amount of people per raft
                                     abs(s(x)-                )             Calculate how close is the current raft
                                                               for x in c   For each raft in the partition
                                   s(                                    )  Sum it (the sum is a score of how close to ideal the function is),
             min(                                                         ) And find the lowest valued partition.

Je suis certain que cela peut être approfondi, en particulier la pfonction, mais j'ai déjà travaillé dessus pendant des heures, alors c'est parti.

sagiksp
la source