[{"data":1,"prerenderedAt":817},["ShallowReactive",2],{"dev-\u002Fdeveloper\u002Fguides\u002Fknapsack":3},{"id":4,"title":5,"body":6,"description":796,"extension":797,"faq":798,"meta":811,"navigation":76,"noindex":812,"path":813,"seo":814,"stem":815,"__hash__":816},"content\u002Fdeveloper\u002Fguides\u002Fknapsack.md","Solve the knapsack problem in Python",{"type":7,"value":8,"toc":790},"minimark",[9,13,26,33,38,41,337,344,366,369,373,388,692,697,701,723,730,734,770,781,786],[10,11,5],"h1",{"id":12},"solve-the-knapsack-problem-in-python",[14,15,16,17,21,22,25],"p",{},"The ",[18,19,20],"strong",{},"knapsack problem"," shows up whenever you must choose a subset under a\nbudget: pick items to maximize total value without exceeding a weight (or cost,\nor time) limit. It is the textbook model behind selecting features under an\neffort budget, ads under a screen limit, or investments under a cash cap. It's\nalso called the ",[18,23,24],{},"0\u002F1 (binary) knapsack"," — subset selection under a budget.",[14,27,28,29,32],{},"The tempting first attempt is to enumerate every subset. The approach that keeps\nworking is to model it and hand it to a solver — with ",[18,30,31],{},"quicopt"," that is a few\nlines of Python.",[34,35,37],"h2",{"id":36},"the-brute-force-trap","The brute-force trap",[14,39,40],{},"\"Try every subset\" translates directly to a loop over all 0\u002F1 combinations:",[42,43,49],"pre",{"className":44,"code":45,"filename":46,"language":47,"meta":48,"style":48},"language-python shiki shiki-themes github-dark","from itertools import product\n\nweights  = [2, 3, 4, 5, 9, 7, 1, 6]\nvalues   = [3, 4, 5, 8, 10, 6, 2, 7]\ncapacity = 15\nN = len(weights)\n\nbest = 0\nfor pick in product([0, 1], repeat=N):\n    if sum(weights[i] for i in range(N) if pick[i]) \u003C= capacity:\n        best = max(best, sum(values[i] for i in range(N) if pick[i]))\nprint(best)  # -> 22\n","brute_force.py","python","",[50,51,52,71,78,133,177,188,202,207,218,252,289,324],"code",{"__ignoreMap":48},[53,54,57,61,65,68],"span",{"class":55,"line":56},"line",1,[53,58,60],{"class":59},"snl16","from",[53,62,64],{"class":63},"s95oV"," itertools ",[53,66,67],{"class":59},"import",[53,69,70],{"class":63}," product\n",[53,72,74],{"class":55,"line":73},2,[53,75,77],{"emptyLinePlaceholder":76},true,"\n",[53,79,81,84,87,90,94,97,100,102,105,107,110,112,115,117,120,122,125,127,130],{"class":55,"line":80},3,[53,82,83],{"class":63},"weights  ",[53,85,86],{"class":59},"=",[53,88,89],{"class":63}," [",[53,91,93],{"class":92},"sDLfK","2",[53,95,96],{"class":63},", ",[53,98,99],{"class":92},"3",[53,101,96],{"class":63},[53,103,104],{"class":92},"4",[53,106,96],{"class":63},[53,108,109],{"class":92},"5",[53,111,96],{"class":63},[53,113,114],{"class":92},"9",[53,116,96],{"class":63},[53,118,119],{"class":92},"7",[53,121,96],{"class":63},[53,123,124],{"class":92},"1",[53,126,96],{"class":63},[53,128,129],{"class":92},"6",[53,131,132],{"class":63},"]\n",[53,134,136,139,141,143,145,147,149,151,153,155,158,160,163,165,167,169,171,173,175],{"class":55,"line":135},4,[53,137,138],{"class":63},"values   ",[53,140,86],{"class":59},[53,142,89],{"class":63},[53,144,99],{"class":92},[53,146,96],{"class":63},[53,148,104],{"class":92},[53,150,96],{"class":63},[53,152,109],{"class":92},[53,154,96],{"class":63},[53,156,157],{"class":92},"8",[53,159,96],{"class":63},[53,161,162],{"class":92},"10",[53,164,96],{"class":63},[53,166,129],{"class":92},[53,168,96],{"class":63},[53,170,93],{"class":92},[53,172,96],{"class":63},[53,174,119],{"class":92},[53,176,132],{"class":63},[53,178,180,183,185],{"class":55,"line":179},5,[53,181,182],{"class":63},"capacity ",[53,184,86],{"class":59},[53,186,187],{"class":92}," 15\n",[53,189,191,194,196,199],{"class":55,"line":190},6,[53,192,193],{"class":63},"N ",[53,195,86],{"class":59},[53,197,198],{"class":92}," len",[53,200,201],{"class":63},"(weights)\n",[53,203,205],{"class":55,"line":204},7,[53,206,77],{"emptyLinePlaceholder":76},[53,208,210,213,215],{"class":55,"line":209},8,[53,211,212],{"class":63},"best ",[53,214,86],{"class":59},[53,216,217],{"class":92}," 0\n",[53,219,221,224,227,230,233,236,238,240,243,247,249],{"class":55,"line":220},9,[53,222,223],{"class":59},"for",[53,225,226],{"class":63}," pick ",[53,228,229],{"class":59},"in",[53,231,232],{"class":63}," product([",[53,234,235],{"class":92},"0",[53,237,96],{"class":63},[53,239,124],{"class":92},[53,241,242],{"class":63},"], ",[53,244,246],{"class":245},"s9osk","repeat",[53,248,86],{"class":59},[53,250,251],{"class":63},"N):\n",[53,253,255,258,261,264,266,269,271,274,277,280,283,286],{"class":55,"line":254},10,[53,256,257],{"class":59},"    if",[53,259,260],{"class":92}," sum",[53,262,263],{"class":63},"(weights[i] ",[53,265,223],{"class":59},[53,267,268],{"class":63}," i ",[53,270,229],{"class":59},[53,272,273],{"class":92}," range",[53,275,276],{"class":63},"(N) ",[53,278,279],{"class":59},"if",[53,281,282],{"class":63}," pick[i]) ",[53,284,285],{"class":59},"\u003C=",[53,287,288],{"class":63}," capacity:\n",[53,290,292,295,297,300,303,306,309,311,313,315,317,319,321],{"class":55,"line":291},11,[53,293,294],{"class":63},"        best ",[53,296,86],{"class":59},[53,298,299],{"class":92}," max",[53,301,302],{"class":63},"(best, ",[53,304,305],{"class":92},"sum",[53,307,308],{"class":63},"(values[i] ",[53,310,223],{"class":59},[53,312,268],{"class":63},[53,314,229],{"class":59},[53,316,273],{"class":92},[53,318,276],{"class":63},[53,320,279],{"class":59},[53,322,323],{"class":63}," pick[i]))\n",[53,325,327,330,333],{"class":55,"line":326},12,[53,328,329],{"class":92},"print",[53,331,332],{"class":63},"(best)  ",[53,334,336],{"class":335},"sAwPA","# -> 22\n",[14,338,339,340,343],{},"Correct for eight items. But the number of subsets is ",[50,341,342],{},"2^N",", which doubles with\nevery item added:",[345,346,347,354,360],"ul",{},[348,349,350,353],"li",{},[18,351,352],{},"8 items"," → 256 subsets (instant)",[348,355,356,359],{},[18,357,358],{},"20 items"," → 1,048,576",[348,361,362,365],{},[18,363,364],{},"40 items"," → 1,099,511,627,776 (over a trillion)",[14,367,368],{},"Enumeration collapses long before instances get interesting.",[34,370,372],{"id":371},"model-it-as-a-milp","Model it as a MILP",[14,374,375,376,379,380,383,384,387],{},"Knapsack is a ",[18,377,378],{},"mixed-integer linear program (MILP)",". A binary variable ",[50,381,382],{},"x[i]","\nsays whether item ",[50,385,386],{},"i"," is taken. One linear constraint keeps the chosen weight\nwithin capacity, and the objective maximizes the chosen value:",[42,389,392],{"className":44,"code":390,"filename":391,"language":47,"meta":48,"style":48},"from ortools.math_opt.python import mathopt\nfrom quicopt import Client\n\n# 0\u002F1 knapsack: pick items to maximize value without exceeding the weight budget.\nweights  = [2, 3, 4, 5, 9, 7, 1, 6]\nvalues   = [3, 4, 5, 8, 10, 6, 2, 7]\ncapacity = 15\nN = len(weights)\n\nmodel = mathopt.Model(name=\"knapsack\")\nx = [model.add_binary_variable(name=f\"x_{i}\") for i in range(N)]\nmodel.add_linear_constraint(sum(weights[i] * x[i] for i in range(N)) \u003C= capacity)\nmodel.maximize(sum(values[i] * x[i] for i in range(N)))\n\nclient = Client(\"https:\u002F\u002Ftry.quicoptapi.pgi.fz-juelich.de\")\nresult = client.solve(model)\nprint(result.display)\n","knapsack.py",[50,393,394,406,418,422,427,467,507,515,525,529,551,596,627,652,657,673,684],{"__ignoreMap":48},[53,395,396,398,401,403],{"class":55,"line":56},[53,397,60],{"class":59},[53,399,400],{"class":63}," ortools.math_opt.python ",[53,402,67],{"class":59},[53,404,405],{"class":63}," mathopt\n",[53,407,408,410,413,415],{"class":55,"line":73},[53,409,60],{"class":59},[53,411,412],{"class":63}," quicopt ",[53,414,67],{"class":59},[53,416,417],{"class":63}," Client\n",[53,419,420],{"class":55,"line":80},[53,421,77],{"emptyLinePlaceholder":76},[53,423,424],{"class":55,"line":135},[53,425,426],{"class":335},"# 0\u002F1 knapsack: pick items to maximize value without exceeding the weight budget.\n",[53,428,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459,461,463,465],{"class":55,"line":179},[53,430,83],{"class":63},[53,432,86],{"class":59},[53,434,89],{"class":63},[53,436,93],{"class":92},[53,438,96],{"class":63},[53,440,99],{"class":92},[53,442,96],{"class":63},[53,444,104],{"class":92},[53,446,96],{"class":63},[53,448,109],{"class":92},[53,450,96],{"class":63},[53,452,114],{"class":92},[53,454,96],{"class":63},[53,456,119],{"class":92},[53,458,96],{"class":63},[53,460,124],{"class":92},[53,462,96],{"class":63},[53,464,129],{"class":92},[53,466,132],{"class":63},[53,468,469,471,473,475,477,479,481,483,485,487,489,491,493,495,497,499,501,503,505],{"class":55,"line":190},[53,470,138],{"class":63},[53,472,86],{"class":59},[53,474,89],{"class":63},[53,476,99],{"class":92},[53,478,96],{"class":63},[53,480,104],{"class":92},[53,482,96],{"class":63},[53,484,109],{"class":92},[53,486,96],{"class":63},[53,488,157],{"class":92},[53,490,96],{"class":63},[53,492,162],{"class":92},[53,494,96],{"class":63},[53,496,129],{"class":92},[53,498,96],{"class":63},[53,500,93],{"class":92},[53,502,96],{"class":63},[53,504,119],{"class":92},[53,506,132],{"class":63},[53,508,509,511,513],{"class":55,"line":204},[53,510,182],{"class":63},[53,512,86],{"class":59},[53,514,187],{"class":92},[53,516,517,519,521,523],{"class":55,"line":209},[53,518,193],{"class":63},[53,520,86],{"class":59},[53,522,198],{"class":92},[53,524,201],{"class":63},[53,526,527],{"class":55,"line":220},[53,528,77],{"emptyLinePlaceholder":76},[53,530,531,534,536,539,542,544,548],{"class":55,"line":254},[53,532,533],{"class":63},"model ",[53,535,86],{"class":59},[53,537,538],{"class":63}," mathopt.Model(",[53,540,541],{"class":245},"name",[53,543,86],{"class":59},[53,545,547],{"class":546},"sU2Wk","\"knapsack\"",[53,549,550],{"class":63},")\n",[53,552,553,556,558,561,563,565,568,571,574,576,579,582,585,587,589,591,593],{"class":55,"line":291},[53,554,555],{"class":63},"x ",[53,557,86],{"class":59},[53,559,560],{"class":63}," [model.add_binary_variable(",[53,562,541],{"class":245},[53,564,86],{"class":59},[53,566,567],{"class":59},"f",[53,569,570],{"class":546},"\"x_",[53,572,573],{"class":92},"{",[53,575,386],{"class":63},[53,577,578],{"class":92},"}",[53,580,581],{"class":546},"\"",[53,583,584],{"class":63},") ",[53,586,223],{"class":59},[53,588,268],{"class":63},[53,590,229],{"class":59},[53,592,273],{"class":92},[53,594,595],{"class":63},"(N)]\n",[53,597,598,601,603,605,608,611,613,615,617,619,622,624],{"class":55,"line":326},[53,599,600],{"class":63},"model.add_linear_constraint(",[53,602,305],{"class":92},[53,604,263],{"class":63},[53,606,607],{"class":59},"*",[53,609,610],{"class":63}," x[i] ",[53,612,223],{"class":59},[53,614,268],{"class":63},[53,616,229],{"class":59},[53,618,273],{"class":92},[53,620,621],{"class":63},"(N)) ",[53,623,285],{"class":59},[53,625,626],{"class":63}," capacity)\n",[53,628,630,633,635,637,639,641,643,645,647,649],{"class":55,"line":629},13,[53,631,632],{"class":63},"model.maximize(",[53,634,305],{"class":92},[53,636,308],{"class":63},[53,638,607],{"class":59},[53,640,610],{"class":63},[53,642,223],{"class":59},[53,644,268],{"class":63},[53,646,229],{"class":59},[53,648,273],{"class":92},[53,650,651],{"class":63},"(N)))\n",[53,653,655],{"class":55,"line":654},14,[53,656,77],{"emptyLinePlaceholder":76},[53,658,660,663,665,668,671],{"class":55,"line":659},15,[53,661,662],{"class":63},"client ",[53,664,86],{"class":59},[53,666,667],{"class":63}," Client(",[53,669,670],{"class":546},"\"https:\u002F\u002Ftry.quicoptapi.pgi.fz-juelich.de\"",[53,672,550],{"class":63},[53,674,676,679,681],{"class":55,"line":675},16,[53,677,678],{"class":63},"result ",[53,680,86],{"class":59},[53,682,683],{"class":63}," client.solve(model)\n",[53,685,687,689],{"class":55,"line":686},17,[53,688,329],{"class":92},[53,690,691],{"class":63},"(result.display)\n",[693,694],"term-result",{":rows":695,"cmd":696},"[\"├── status:     optimal\",\"├── feasible:   true\",\"├── objective:  22.0\",\"├── x:          x_0=1, x_1=1, x_2=1, x_3=1, x_4=0, x_5=0, …  (8 variables)\",\"└── solve_time: 0.0104 s\"]","$ python knapsack.py",[34,698,700],{"id":699},"what-you-get","What you get",[14,702,703,706,707,710,711,714,715,718,719,722],{},[50,704,705],{},"status: optimal"," means the solver ",[18,708,709],{},"proved"," no subset does better — value ",[50,712,713],{},"22",",\ntaking items ",[50,716,717],{},"0, 1, 2, 3, 6"," for a total weight of exactly ",[50,720,721],{},"15",". It matches the\nbrute-force answer and comes back in about a hundredth of a second.",[14,724,725,726,729],{},"The difference is scaling. Brute force is ",[50,727,728],{},"O(2^n)","; a MILP solver uses bounds and\nbranch-and-cut to prune the search instead of walking it. The same eight-line\nmodel that handles eight items handles eight hundred — you change the data, not\nthe method.",[34,731,733],{"id":732},"next","Next",[345,735,736,744,756,763],{},[348,737,738,739],{},"The problem class behind this: ",[740,741,743],"a",{"href":742},"\u002Fproblems\u002Fmilp","MILP",[348,745,746,747,751,752],{},"Related guides: ",[740,748,750],{"href":749},"\u002Fdeveloper\u002Fguides\u002Fset-cover","Set cover"," · ",[740,753,755],{"href":754},"\u002Fdeveloper\u002Fguides\u002Fbin-packing","Bin packing",[348,757,758,759],{},"A runnable model for every supported class: ",[740,760,762],{"href":761},"\u002Fdeveloper\u002Fexamples","Examples",[348,764,765,766],{},"Set up the client and solve your first model: ",[740,767,769],{"href":768},"\u002Fdeveloper\u002Fgetting-started","Getting started",[14,771,772,775,776,780],{},[18,773,774],{},"Reference:"," S. Martello and P. Toth, ",[777,778,779],"em",{},"Knapsack Problems: Algorithms and\nComputer Implementations",", Wiley, 1990.",[782,783],"contact-cta",{"sub":784,"title":785},"Tell us what you're optimizing. We'll help you model it and point you at the right approach.","A bigger selection problem — or a different one?",[787,788,789],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":48,"searchDepth":73,"depth":73,"links":791},[792,793,794,795],{"id":36,"depth":73,"text":37},{"id":371,"depth":73,"text":372},{"id":699,"depth":73,"text":700},{"id":732,"depth":73,"text":733},"Pick the highest-value set of items within a weight budget in Python — the 0\u002F1 knapsack problem modeled as a MILP and solved with quicopt in a few lines, instead of brute-forcing 2^n subsets.","md",[799,802,805,808],{"q":800,"a":801},"Do I need dynamic programming?","No. A MILP solver prunes with bounds and branch-and-cut and scales past DP's pseudo-polynomial table — and it handles extra constraints DP can't.",{"q":803,"a":804},"What about multiple constraints (multi-dimensional knapsack)?","Add one linear capacity constraint per dimension — weight, volume, cost. Same model, more constraints.",{"q":806,"a":807},"Is this 0\u002F1 or bounded knapsack?","This is 0\u002F1 (each item taken or not). For bounded knapsack, make x an integer variable with an upper bound instead of binary.",{"q":809,"a":810},"Is quicopt free to use?","Yes — pip install quicopt and your first call sets up a free key, no license.",{},false,"\u002Fdeveloper\u002Fguides\u002Fknapsack",{"title":5,"description":796},"developer\u002Fguides\u002Fknapsack","wJ3e0nZa3gF_2YgCdluYPUYNxsJm1dHu5xFrHXzJOS8",1784110685995]