python - Python编程问题——确定阅读文章的正确组合以实现最大的智力价值
问题描述
最近遇到一个这样的编程问题。
一套可以看的白皮书,比如说4。页数分别是4、4、6、8。每篇文章的知识价值分别是2、4、4、5。一个人最多只能阅读 15 页。您会选择阅读哪些文章,以最大限度地提高获得的知识价值?
我想出的Python解决方案如下。如果我将 article_count 增加到 20,则程序需要更长的时间才能完成。你能帮忙提出优化它的方法吗?1. 内存效率更高 2. 消除组合,从而减少迭代 3. 算法/数据结构改进
from memory_profiler import profile
from itertools import combinations
import numpy as np
@profile
def max_iv(articles_count, pages, ivs, pages_read_limit):
max_iv = 0
for lgth in range(1,len(pages)+1):
reading_options = combinations(range(len(pages)),lgth)
for ro in reading_options:
if sum((pages[x] for x in ro)) <= pages_read_limit:
iv = sum((ivs[x] for x in ro))
if iv > max_iv:
max_iv = iv
print(max_iv)
if __name__ == '__main__':
articles_count = 4
pages = np.random.randint(1,10,articles_count)
ivs=np.random.randint(1,10,articles_count)
pages_read_limit = 15
max_iv(articles_count, pages, ivs, pages_read_limit)
解决方案
这是一个有趣的问题,虽然不是真正的 StackOverflow 问题,但我无法抗拒:)。
下面是一个提供我自己的解决方案的解决方案,一个简单的蛮力解决方案(检查结果),以及每个解决方案执行 10,000 次所需的时间:
from random import randint
from collections import namedtuple
from itertools import combinations
from timeit import timeit
Article = namedtuple('Article', ['id', 'pages', 'value'])
def get_articles(min_pages, max_pages, min_value, max_value, number_of_articles):
return [Article(n, randint(min_pages, max_pages), randint(min_value, max_value)) for n in range(number_of_articles)]
def _sort_articles_by_relative_value(articles):
return list(reversed(sorted(articles, key=lambda a: a.value / a.pages)))
def _pick_articles_recursive(articles, pages_read_limit, n=0, picked=None, pages=0, value=0):
if picked is None:
picked = []
while n < len(articles):
if pages + articles[n].pages == pages_read_limit:
return pages_read_limit, value + articles[n].value, picked + [articles[n]]
elif pages + articles[n].pages < pages_read_limit:
inclusive = _pick_articles_recursive(
articles, pages_read_limit, n+1,
picked + [articles[n]], pages + articles[n].pages, value + articles[n].value)
if inclusive[0] == pages_read_limit:
return inclusive
else:
exclusive = _pick_articles_recursive(
articles, pages_read_limit, n+1,
picked, pages, value)
if inclusive[1] > exclusive[1]:
return inclusive
else:
return exclusive
n += 1
return pages, value, picked
def pick_articles(articles, pages_read_limit):
articles = _sort_articles_by_relative_value(articles)
return _pick_articles_recursive(articles, pages_read_limit)
def pick_articles_brute(articles, pages_read_limit):
bv = 0
bp = []
for n in range(len(articles)+1):
for picked_articles in combinations(articles, n):
nv = sum([a.value for a in picked_articles])
np = sum([a.pages for a in picked_articles])
if nv > bv and np <= pages_read_limit:
bp = picked_articles
bv = nv
return sum([a.pages for a in bp]), bv, bp
def max_iv(pages, ivs, pages_read_limit):
max_iv = 0
for lgth in range(1,len(pages)+1):
reading_options = combinations(range(len(pages)),lgth)
for ro in reading_options:
if sum((pages[x] for x in ro)) <= pages_read_limit:
iv = sum((ivs[x] for x in ro))
if iv > max_iv:
max_iv = iv
def main():
articles = get_articles(min_pages=1, max_pages=5, min_value=1, max_value=5, number_of_articles=6)
result_new = pick_articles(articles, pages_read_limit=10)
result_brute = pick_articles_brute(articles, pages_read_limit=10)
print('articles :', articles)
print('result :', result_new)
print('brute :', result_brute)
print('t result :', timeit(lambda: pick_articles(articles, pages_read_limit=10), number=10000))
print('t brute :', timeit(lambda: pick_articles_brute(articles, pages_read_limit=10), number=10000))
print('t max_iv :', timeit(lambda: max_iv(
pages=[a.pages for a in articles], ivs=[a.value for a in articles], pages_read_limit=10), number=10000))
main()
一个典型的结果:
articles : [Article(id=0, pages=1, value=5), Article(id=1, pages=2, value=2), Article(id=2, pages=3, value=4), Article(id=3, pages=4, value=3), Article(id=4, pages=5, value=3), Article(id=5, pages=2, value=4)]
result : (10, 16, [Article(id=0, pages=1, value=5), Article(id=5, pages=2, value=4), Article(id=2, pages=3, value=4), Article(id=3, pages=4, value=3)])
brute : (10, 16, (Article(id=0, pages=1, value=5), Article(id=2, pages=3, value=4), Article(id=3, pages=4, value=3), Article(id=5, pages=2, value=4)))
t result : 0.0626382
t brute : 0.6400916000000001
t max_iv : 0.5818494999999999
请注意,它并不总是表现得很好,但总是比其他两个好。给出的结果相当典型。另请注意,结果不一定与蛮力解决方案相同,但值会相同。在某些情况下,不同的方法会找到不同数量的文章,甚至在极少数情况下甚至会找到不同数量的页面,但它们总是会找到最佳分数。
最后,请注意_sort_articles_by_relative_value
并_pick_articles_recursive
一起给您答案,其余代码只是为了进行比较并使事情看起来不错。
该解决方案之所以有效,是因为它首先计算每篇文章的每页平均值,然后将文章从每页的最大值到每页的最小值进行排序。考虑到这一点,如果你从左到右选择文章,一旦达到最大页数,就没有必要尝试与右边文章的其他组合,因为每页的平均值任何组合都将始终较低,并且总页数不能超过最大值。
推荐阅读
- javascript - 在jQuery中访问列表中项目的css
- drop-down-menu - Word 中的下拉列表看起来不像
- java - 如何指导 javapackager 进行无头 debian 包
- ruby-on-rails - 减少 Rails 请求中对多个关联级别的查询
- javascript - 无法读取 .subscribe 中的属性
- python - 如何在 Pygame 中让 Sprite blitting 两次有两个单独的碰撞框?
- node.js - mongo 元素按日期匹配
- java - Spring/Thymeleaf - 无法将“java.lang.String”类型的值转换为所需类型
- google-sheets - Google Sheets IMPORTRANGE 在一个单元格中有效,在下一个单元格中无效
- firebase - “除非”Firestore 查询?