鱼C论坛

 找回密码
 立即注册
查看: 1146|回复: 10

[已解决]求解:数学问题

[复制链接]
发表于 2022-1-2 17:00:35 | 显示全部楼层 |阅读模式
5鱼币
已知一数组N内有n个数[x1,x2,……xn],给定一个数M,求数组N内有m种组合方式,使得组合之和等于M,m的值等于多少?且每种组合具体情况是什么?
举例:数组[1,2,3,4,5,6,7,8,9,10],若M=10,则m=10,具体如下:
1.[10]
2.[1,9]
3.[2,8]
4.[3,7]
5.[4,6]
6.[1,2,7]
7.[1,3,6]
8.[1,4,5]
9.[2,3,5]
10.[1,2,3,4]
最佳答案
2022-1-2 17:00:36
本帖最后由 Stubborn 于 2022-1-4 13:07 编辑
Stubborn 发表于 2022-1-3 16:52
数据量大,没有办法做到很大的优化。两个核心思想。
一。对于有序数组arry。搜索两个元素和值为M。由两 ...


已知的BUG,
在92行处理idx索引有问题,导致计算元素重复。待修,其他bug,待数据集测试。批注打印的结果是三个组合以上数组比如1,2,7
已修复BUG。90行代码。idx = 0 修改为 idx = width - 1

已知的BUG,
对于重复数据,在搜索两个数的的组合会有忽略的情况,发生在two_sum里面。

对于最后的结果集和bug题主自行尝试修复,回帖就这样,拜拜
可以优化的地方,在最后双层嵌套循环的时候,res = two_sum(array[idx:index], target - sum(each))  ,这个index应该需要跟新,而不是固定的
比如当内层循环全排列的时候,each = (2,)的时候,idx=2, index=没有到理想的值,导致two_sum的搜索范围变大,这个值是可以通过search_index来获取的

避免重复计算
  1.     # TODO 由3个组合,推导到最长组合
  2.     # TODO _index 是两个组合的最大索引值,由于是成对存在,所以 // 2
  3.     index = search_index(array, target)

  4.     for width in range(1, max_length - 1):
  5.         idx = width - 1
  6.         for each in combinations(array[:index // 2], width):
  7.             idx += 1
  8.             res = two_sum(array[idx:index], target - sum(each))
  9.             if not res:
  10.                 break
  11.             result += [list(each)+ e for e in res]
  12.     return len(result), result
复制代码

  1. from itertools import combinations
  2. #
  3. # ls = [1,2,3,4,5]
  4. # print(list(c(ls, 2)))
  5. from typing import List


  6. def two_sum(array: List[int], target: int):
  7.     """给定数组array,和目标值target,搜索所有的两个元素组合和值等于target"""
  8.     result = []
  9.     # TODO 如果当前数组0,1两个元素和大于target,或者0,-1两个元素和小于target
  10.     if len(array) <= 2:
  11.         return result
  12.    
  13.     if array[0] + array[1] > target or array[-2] + array[-1] < target:
  14.         return result

  15.     left, right = 0, len(array) - 1
  16.     while left < right:
  17.         value = array[left] + array[right]
  18.         if value == target:
  19.             result.append([array[left], array[right]])
  20.             left += 1
  21.         elif value > target:
  22.             right -= 1
  23.         else:
  24.             left += 1
  25.     return result


  26. def search_index(array: List[int], target: int):
  27.     """
  28.     给定一个数组,搜索一个缩影,使得 array[index] < target - array[0] 并且 array[index+1] > target - array[0]
  29.         如果没有搜索到,责返回一个None
  30.     """
  31.     left, right = 0, len(array)-1
  32.     # TODO 如果当前数组的最后一个小于target,直接返回最大索引
  33.     if array[right] < target:
  34.         return len(array)
  35.     # TODO 如果当前数组的第一个大于target,直接返回 None
  36.     elif array[0] > target:
  37.         return

  38.     target = target - array[0]
  39.     while left <= right:
  40.         mid = (left + right) // 2
  41.         if array[mid] <= target < array[mid + 1]:
  42.             return mid
  43.         elif array[mid] > target:
  44.             right = mid - 1
  45.         else:
  46.             left = mid + 1


  47. def get_all(array: List[int], target: int):
  48.     """
  49.     给定一个数组array, 一个定值target, 从array中选出n个元素,使得Sn = target, 求所有组合
  50.         array是一个有序,有重复元素的数组
  51.     """
  52.     result = []
  53.     # TODO 处理一个元素的组合
  54.     num = array.count(target)
  55.     if num:
  56.         result += [target for _ in range(num)]

  57.     # TODO max_length为最大组合长度,
  58.     max_length = 0
  59.     for index in range(len(array)):
  60.         value = sum(array[:index])
  61.         if value + array[index + 1] > target:
  62.             max_length = index + 1
  63.             break

  64.     # TODO 如果没有符合的选项
  65.     if max_length == 0:
  66.         return 0, []
  67.     # TODO 如果数组最大长度为1
  68.     elif max_length == 1:
  69.         return len(result), result
  70.     # TODO 如果数组最大长度为2
  71.     elif max_length == 2:
  72.         result += two_sum(array, target)
  73.         return len(result), result

  74.     # TODO 由3个组合,推导到最长组合
  75.     # TODO _index 是两个组合的最大索引值,由于是成对存在,所以 // 2
  76.     index = search_index(array, target)

  77.     for width in range(1, max_length-1):
  78.         idx = width - 1
  79.         for each in combinations(array[:index//2], width):
  80.             idx += 1
  81.             res = two_sum(array[idx:index], target - sum(each))
  82.             # _index = search_index(array[: _index], target - sum_each)
  83.             # res = two_sum(array[1:_index+1], target - sum_each)
  84.             if res:
  85.                 print(each, res)

  86. nums = [1,2,3,4,5,6,7,8,9,10]
  87. M = 20
  88. print(nums[1:10])
  89. get_all(nums, M)

复制代码

最佳答案

查看完整内容

已知的BUG, 在92行处理idx索引有问题,导致计算元素重复。待修,其他bug,待数据集测试。批注打印的结果是三个组合以上数组比如1,2,7 已修复BUG。90行代码。idx = 0 修改为 idx = width - 1 已知的BUG, 对于重复数据,在搜索两个数的的组合会有忽略的情况,发生在two_sum里面。 对于最后的结果集和bug题主自行尝试修复,回帖就这样,拜拜 可以优化的地方,在最后双层嵌套循环的时候,res = two_sum(a ...
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-2 17:00:36 | 显示全部楼层    本楼为最佳答案   
本帖最后由 Stubborn 于 2022-1-4 13:07 编辑
Stubborn 发表于 2022-1-3 16:52
数据量大,没有办法做到很大的优化。两个核心思想。
一。对于有序数组arry。搜索两个元素和值为M。由两 ...


已知的BUG,
在92行处理idx索引有问题,导致计算元素重复。待修,其他bug,待数据集测试。批注打印的结果是三个组合以上数组比如1,2,7
已修复BUG。90行代码。idx = 0 修改为 idx = width - 1

已知的BUG,
对于重复数据,在搜索两个数的的组合会有忽略的情况,发生在two_sum里面。

对于最后的结果集和bug题主自行尝试修复,回帖就这样,拜拜
可以优化的地方,在最后双层嵌套循环的时候,res = two_sum(array[idx:index], target - sum(each))  ,这个index应该需要跟新,而不是固定的
比如当内层循环全排列的时候,each = (2,)的时候,idx=2, index=没有到理想的值,导致two_sum的搜索范围变大,这个值是可以通过search_index来获取的

避免重复计算
  1.     # TODO 由3个组合,推导到最长组合
  2.     # TODO _index 是两个组合的最大索引值,由于是成对存在,所以 // 2
  3.     index = search_index(array, target)

  4.     for width in range(1, max_length - 1):
  5.         idx = width - 1
  6.         for each in combinations(array[:index // 2], width):
  7.             idx += 1
  8.             res = two_sum(array[idx:index], target - sum(each))
  9.             if not res:
  10.                 break
  11.             result += [list(each)+ e for e in res]
  12.     return len(result), result
复制代码

  1. from itertools import combinations
  2. #
  3. # ls = [1,2,3,4,5]
  4. # print(list(c(ls, 2)))
  5. from typing import List


  6. def two_sum(array: List[int], target: int):
  7.     """给定数组array,和目标值target,搜索所有的两个元素组合和值等于target"""
  8.     result = []
  9.     # TODO 如果当前数组0,1两个元素和大于target,或者0,-1两个元素和小于target
  10.     if len(array) <= 2:
  11.         return result
  12.    
  13.     if array[0] + array[1] > target or array[-2] + array[-1] < target:
  14.         return result

  15.     left, right = 0, len(array) - 1
  16.     while left < right:
  17.         value = array[left] + array[right]
  18.         if value == target:
  19.             result.append([array[left], array[right]])
  20.             left += 1
  21.         elif value > target:
  22.             right -= 1
  23.         else:
  24.             left += 1
  25.     return result


  26. def search_index(array: List[int], target: int):
  27.     """
  28.     给定一个数组,搜索一个缩影,使得 array[index] < target - array[0] 并且 array[index+1] > target - array[0]
  29.         如果没有搜索到,责返回一个None
  30.     """
  31.     left, right = 0, len(array)-1
  32.     # TODO 如果当前数组的最后一个小于target,直接返回最大索引
  33.     if array[right] < target:
  34.         return len(array)
  35.     # TODO 如果当前数组的第一个大于target,直接返回 None
  36.     elif array[0] > target:
  37.         return

  38.     target = target - array[0]
  39.     while left <= right:
  40.         mid = (left + right) // 2
  41.         if array[mid] <= target < array[mid + 1]:
  42.             return mid
  43.         elif array[mid] > target:
  44.             right = mid - 1
  45.         else:
  46.             left = mid + 1


  47. def get_all(array: List[int], target: int):
  48.     """
  49.     给定一个数组array, 一个定值target, 从array中选出n个元素,使得Sn = target, 求所有组合
  50.         array是一个有序,有重复元素的数组
  51.     """
  52.     result = []
  53.     # TODO 处理一个元素的组合
  54.     num = array.count(target)
  55.     if num:
  56.         result += [target for _ in range(num)]

  57.     # TODO max_length为最大组合长度,
  58.     max_length = 0
  59.     for index in range(len(array)):
  60.         value = sum(array[:index])
  61.         if value + array[index + 1] > target:
  62.             max_length = index + 1
  63.             break

  64.     # TODO 如果没有符合的选项
  65.     if max_length == 0:
  66.         return 0, []
  67.     # TODO 如果数组最大长度为1
  68.     elif max_length == 1:
  69.         return len(result), result
  70.     # TODO 如果数组最大长度为2
  71.     elif max_length == 2:
  72.         result += two_sum(array, target)
  73.         return len(result), result

  74.     # TODO 由3个组合,推导到最长组合
  75.     # TODO _index 是两个组合的最大索引值,由于是成对存在,所以 // 2
  76.     index = search_index(array, target)

  77.     for width in range(1, max_length-1):
  78.         idx = width - 1
  79.         for each in combinations(array[:index//2], width):
  80.             idx += 1
  81.             res = two_sum(array[idx:index], target - sum(each))
  82.             # _index = search_index(array[: _index], target - sum_each)
  83.             # res = two_sum(array[1:_index+1], target - sum_each)
  84.             if res:
  85.                 print(each, res)

  86. nums = [1,2,3,4,5,6,7,8,9,10]
  87. M = 20
  88. print(nums[1:10])
  89. get_all(nums, M)

复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-2 17:32:26 | 显示全部楼层
小数目还行,大数目千万不要这样搞,除非你的电脑是超级电脑:
  1. from itertools import permutations as p

  2. arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  3. ans = []
  4. res = []
  5. M = 10
  6. N = len(arr)

  7. for i in range(1, N+1):
  8.     [ans.append(sorted(list(i))) for i in p(arr, i) if sum(list(i)) == M]

  9. for i in ans:
  10.     if i not in res:
  11.         res.append(i)

  12. for i in res:
  13.     print(i)
复制代码
输出结果:
  1. [10]
  2. [1, 9]
  3. [2, 8]
  4. [3, 7]
  5. [4, 6]
  6. [1, 2, 7]
  7. [1, 3, 6]
  8. [1, 4, 5]
  9. [2, 3, 5]
  10. [1, 2, 3, 4]
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-1-2 22:34:47 | 显示全部楼层
傻眼貓咪 发表于 2022-1-2 17:32
小数目还行,大数目千万不要这样搞,除非你的电脑是超级电脑:输出结果:

非常感谢!这个只是我举的简单例子。其实我想要的是针对大量数据的通用巧妙算法!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-3 00:11:59 | 显示全部楼层
  1. >>> from itertools import combinations as c
  2. >>> ls = [1,2,3,4,5,6,7,8,9,10]
  3. >>> def func(ls, M=10):
  4.         res = []
  5.         for i in range(1, len(ls)+1):
  6.                 for j in c(ls, i):
  7.                         if sum(j) == M:
  8.                                 res.append(j)
  9.         return res, len(res)

  10. >>> func(ls, M=10)
  11. ([(10,), (1, 9), (2, 8), (3, 7), (4, 6), (1, 2, 7), (1, 3, 6), (1, 4, 5), (2, 3, 5), (1, 2, 3, 4)], 10)
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-3 08:59:12 | 显示全部楼层
本帖最后由 Stubborn 于 2022-1-3 09:39 编辑

数组是否有重复元素,是否是有序的数组,排序好的数据
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-1-3 09:56:55 | 显示全部楼层
Stubborn 发表于 2022-1-3 08:59
数组是否有重复元素,是否是有序的数组,排序好的数据

有重复元素,先有序吧。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-3 16:52:40 | 显示全部楼层
wmjrty 发表于 2022-1-3 09:56
有重复元素,先有序吧。

数据量大,没有办法做到很大的优化。两个核心思想。
一。对于有序数组arry。搜索两个元素和值为M。由两侧同时向中心搜索,会更快(双指针)。
二。要N个里面选出n个元素,使得Sn = M 。肯定会用到全排,但是是有序的数组,所以n的最大值是可以被确定的。可以搜索一段连续的数据N[j:k],有k - j = n和Sjk >= M。用来确保避免重复的排列搜索工具,具体代码,晚些时间在贴上

      
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-3 18:41:29 | 显示全部楼层
排序, 去重, 缺几找几(动态规划)
没了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-1-3 21:55:23 From FishC Mobile | 显示全部楼层
Ly_悠然天下 发表于 2022-1-3 18:41
排序, 去重, 缺几找几(动态规划)
没了

应该不能绝对去重的。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-1-4 19:42:29 | 显示全部楼层
wmjrty 发表于 2022-1-3 09:56
有重复元素,先有序吧。

#10楼跟新代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-14 03:20

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表