主题:多个数值怎样凑成一个要求的数值?
holiwood
[专家分:0] 发布于 2011-12-22 21:58:00
请教怎样在多数据中快速选择出几个数值合并成一个固定的数值?
要求在100多个数值中快速挑选出最佳的数值合并成一个已固定数值。(不一定和固定值完一样只要尽可能相近就行,每个数只能选一次。)
我用顺序相加当和固定值相近时然后用if语句从余下的数值中选出需要的数值再相加,这样总觉得不是最佳的办法。请问高手是否有更好的办法?先谢谢了!
如附件中选出A1的数值相加最后得420000.00最相近的数该怎样编程?
回复列表 (共8个回复)
沙发
holiwood [专家分:0] 发布于 2011-12-23 13:00:00
[b]这道题真这么难吗?[/b]
板凳
moz [专家分:37620] 发布于 2011-12-23 22:15:00
在很久很久以前,我在工作中遇到类似的问题.
在做假帐的时候,我会遇到一个虚报的费用,
然后我需要在一大堆需要报销的收据里面,
挑出若干张费用收据,凑成那个虚报的费用,
以便填帐,如果实在找不到相等的结果,就拿最接近的结果进行涂改增补之类的.
我记得那时候,我还专门拿EXCEL来写了一个程序,现在找不到了,
我就大概说一下当初的想法,自己整理一下,程序就出来了:
1.排序,从大到小排
2.比目标值S大的加数,全部剔除
3.从大到小开始相加,N9+N8+N7....
4.如果等于目标值S,就有结果了.
5.如果比S小,继续加下一个数.
6.如果比S大,最后一个加数换下一个.假如这时候是N5,就把最后一个加数N5,换成N4....一直到N1
7.如果加到最后一个值N1,还是比S大,倒回倒数第二个加数往后类推.
8.直至第一个加数开始到最后一个加数之和,仍比S小,为止.例如:(N5+N4+....+N1<S)
9.至于最接近序列,可以在比较过程中保存差的绝对值最小的一组加数.
3 楼
moz [专家分:37620] 发布于 2011-12-24 00:36:00
我把程序写出来之后,(这个代码我得等到明年再贴上来)
才发现一个问题:
表中的加数,最大的是五千多,最小是九百多,个数是173个,要的和是42万,
粗略估计了一下,需要一百多个加数.
我用VFP写出代码之后运行的时候,一直很疑惑怎么答案还不出来?
然后调试一下,前面一百多个加数一直没变化,循环还在后面的几十个数里面跳动.
然后我才想到,这个个数太多了,和与加数之间的差距太大了.
于是乎,我sum了一下,数据表中的总和是451649.22
居然跟42万差不远啊.哈哈,这下子问题变得简单了.
451649.22-420000=31649.22
只要找到加数的和接近31649.22
把这些加数排除后,剩下的加数和岂不是接近42万了?
答案马上就出来了.
5336.60+4928.81+4593.67+4583.25+4486.88+4355.22+3364.02=31648.45≈31649.22
4 楼
holiwood [专家分:0] 发布于 2011-12-26 16:10:00
[b]去这里看看,panpende老师的代码相当不错。[/b]
[url=http://bbs.bccn.net/thread-358216-2-1.html]http://bbs.bccn.net/thread-358216-2-1.html[/url]
5 楼
moz [专家分:37620] 发布于 2011-12-27 01:31:00
呵呵,过不了年了.
以下方法都是用的普通遍历.
利用字符串:
USE K:\test.dbf
SORT TO K:\tes2.dbf ON A1 /D
USE K:\tes2.dbf
ss=420000
*ss=31649.22
r1=STR(1,10)
s1="0+"+STR(A1,10,2)
s3=s1
t2=ss
DO WHILE !EOF()
t1=EVALUATE(s1)
IF ABS(t1-ss)<t2 THEN
t2=ABS(t1-ss)
s3=s1
ENDIF
IF t1=ss THEN
?STRTRAN(SUBSTR(s1,3)+"="+STR(ss,10,2)," ","")
*EXIT
ENDIF
IF t1<ss AND VAL(RIGHT(r1,10))<RECCOUNT() THEN
r1=r1+RIGHT(r1,10)
s1=s1+"+"+RIGHT(s1,10)
ENDIF
r2=VAL(RIGHT(r1,10))
r1=LEFT(r1,LEN(r1)-10)
s1=LEFT(s1,RAT("+",s1)-1)
IF r2=RECCOUNT() THEN
r2=VAL(RIGHT(r1,10))
r1=LEFT(r1,LEN(r1)-10)
s1=LEFT(s1,RAT("+",s1)-1)
IF "0"=s1 AND r2=RECCOUNT() THEN
EXIT
ENDIF
ENDIF
GOTO r2+1
r1=r1+STR(r2+1)
s1=s1+"+"+STR(A1,10,2)
ENDDO
IF t2>0 THEN
?STRTRAN(SUBSTR(s3,3)+"="+ALLTRIM(STR(EVALUATE(s3),10,2))+"≈"+ALLTRIM(STR(ss,10,2))," ","")
ENDIF
利用数据表.
USE K:\test.dbf
SELECT *,000 as A3 FROM test ORDER BY 1 DESC INTO TABLE K:\tes3.dbf
SELECT tes3
GOTO TOP
replace A3 WITH -1
ss=420000
*ss=31649.22
s1=A1
r1=1
DO WHILE !(s1=ss)
IF s1>ss THEN
r1=A3
replace A3 WITH 0
s1=s1-A1
ENDIF
SKIP
DO WHILE EOF()
GOTO r1
r1=A3
replace A3 WITH 0
IF r1=-1 THEN
s1=ss
EXIT
ENDIF
s1=s1-A1
GOTO r1
r1=A3
replace A3 WITH 0
s1=s1-A1
SKIP
ENDDO
replace A3 WITH r1
r1=RECNO()
s1=s1+A1
ENDDO
SELECT A1 FROM tes3 WHERE A3<>0
数组比表更快:
USE K:\test.dbf
SELECT A1 FROM test ORDER BY 1 DESC INTO ARRAY Y1
N=_tally
ss=420000
*ss=31649.22
DIMENSION r(N)
k=1
r(k)=1
s1=Y1(r(1))
DO WHILE !(s1=ss)
IF s1>ss THEN
s1=s1-Y1(r(k))
r(k)=r(k)+1
ELSE
k=k+1
r(k)=r(k-1)+1
ENDIF
DO WHILE r(K)>N
IF k=1 THEN
s1=ss
EXIT
ENDIF
k=k-1
s1=s1-Y1(r(k))
r(k)=r(k)+1
ENDDO
s1=s1+Y1(r(k))
ENDDO
s2=STR(Y1(r(1)),10,2)
FOR i=2 TO k
s2=s2+"+"+STR(Y1(r(i)),10,2)
NEXT
s2=STRTRAN(s2+"="+STR(EVALUATE(s2),10,2)," ","")
?s2
最后一个是以递归方式调用,但最大嵌套数为126,这个题目超出了要求,不过可以以差为目标值:
*USE K:\test.dbf
SELECT A1 FROM test ORDER BY 1 DESC INTO ARRAY Y1
N=_tally
ss=31649.22
s1=SUBSTR(Sums(1,ss),2)
?STRTRAN(s1+"="+STR(EVALUATE(s1),10,2)," ","")
FUNCTION Sums(k,s0)
LOCAL i,k,s0,s2
FOR i=k TO N
DO CASE
CASE Y1(i)=s0
RETURN "+"+STR(Y1(i),10,2)
CASE Y1(i)<s0
s2=Sums(i+1,s0-Y1(i))
IF ""<s2 THEN
RETURN "+"+STR(Y1(i),10,2)+s2
ENDIF
ENDCASE
NEXT
RETURN ""
ENDFUNC
6 楼
moz [专家分:37620] 发布于 2011-12-28 22:07:00
再次修改:
USE test.dbf
N=RECCOUNT()
ss=420000
*ss=31649.22
COPY TO ARRAY Y1 FIELDS A1
=ASORT(Y1,1,-1,1)
DIMENSION r(N)
k=1
r(k)=1
s1=Y1(r(k))
DO WHILE !(s1=ss)
IF s1>ss THEN
s1=s1-Y1(r(k))
r(k)=r(k)+1
ELSE
k=k+1
r(k)=r(k-1)+1
ENDIF
DO WHILE r(k)>N
IF k=1 THEN
return
ENDIF
k=k-1
s1=s1-Y1(r(k))
r(k)=r(k)+1
ENDDO
s1=s1+Y1(r(k))
ENDDO
s2=STR(Y1(r(1)),10,2)
FOR i=2 TO k
s2=s2+"+"+STR(Y1(r(i)),10,2)
NEXT
?STRTRAN(s2+"="+STR(EVALUATE(s2),10,2)," ","")
比起来,速度有点慢,但也许是偶然因素,把目标值减少一个零,就比他的快.
我写的这些代码方法,理论上好像都是叫做"遍历"吧,我不太懂理论和数据.
好像应该还有更好更快的数据处理办法,但我的水平,也就到此为止了.
用了两个小时,得到了3245个结果,现在还没完......
7 楼
holiwood [专家分:0] 发布于 2011-12-30 22:06:00
moz老师谢谢你!你的回帖的代码相当不错,学习了。
8 楼
moz [专家分:37620] 发布于 2011-12-30 23:43:00
算到三万多个结果的时候,我的电脑死掉了.
如果后面加多一个零,程序要出错了,应该要在前面加入目标值的限制 [和,最小值]
我来回复