乘积算法问题

ckok 2009-07-29 11:09:46
已知a,b,c,d四个数都1到16之间的整型,即它们的最大乘积为65536,现随便取1..65536之间的数R,要求得到abcd四个数,使它们的乘积最接近给定的数R
例如R=9009 可得到四个数分别为13,11,9,7
又如R=28 得到四个数为7,4,1,1或7,2,2,1(此时得到任何一种组合即可)
当没有合适的组合时要求最接近
如R=17 得到四个数为6,3,1,1或8,2,1,1等

希望大家给个算法 谢谢!
...全文
134 点赞 收藏 19
写回复
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
绿色夹克衫 2009-07-31
思路挺简单的,就是先读入一个数n,然后试着把n分解为4个16以内的数相乘,如果可以分解,就直接输出分解结果,
如果不能分解继续试n-1和n+1,n-2,n+2,n-3,n+3......直到可以分解为止。

测试一个数是否能分解为4个16以内的数相乘,只要该数小于65536(这点我没有判断),然后从16开始试除就行(n % i = 0),
再试除15,14......1,如果一个数不能写为4个16以内的数相乘,则这个数一定含有大于16的质因子,除到最后结果肯定不是1
反之除到最后肯定是1(我用的是<=16判断的,可以稍微快一些)

[Quote=引用 12 楼 LeonTown 的回复:]
没看明白。
大侠给讲一下吧。

引用 8 楼 litaoye 的回复:

给段简单的代码吧


C# code
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());

            int i = 0;

            while (true)
            {
                if(factorization(n + i))
                {
   …
[/Quote]
回复
acdbxzyw 2009-07-31
我说的最简单是四重循环,话糙理不糙。
你可以预先循环完了保存一张表,然后随时二分查询就行了。
唉 又混个没分,真是打击人的积极性。

分解因子的效率根本就比较低。
四重循环从各个方向是递增的,处理一下也能达到效果。
预处理就更好了。
回复
绿色夹克衫 2009-07-31
不好意思,程序有个BUG,问题出现在4个数上,如果用5^5或7^5来测,我的程序会返回true,实际上是false
应该加一个计数,修改一下factorization函数!


public static bool factorization(int m)
{
int count = 0;
for (int i = 16; i > 1; i--)
{
while (m % i == 0)
{
m /= i;
count++;
}

if (count > 4)
return false;

if (m <= 16)
return true;
}

return false;
}
回复
ckok 2009-07-31
谢谢各位 其实现在已经说不上是什么算法了 反正以满足实际使用为目的 经过改进 在创建乘积表时顺便也用了数组保存了0..65536之间的每个数最近的一个位置的乘积组合的偏差,另外 进一步将数值的组合数的数组缩小到1200 因为实际有组合的数值只有1100个左右
反正现在当需要一个组合时只需要由当前数值加上偏差 然后直接从数组中取就可以了

谢谢大家 谢谢CSDN的高手们!结贴
回复
ckok 2009-07-31
因为根据演算 65536个数中有64401个数都是无法分解的 那进行乘积接近就比较麻烦 所以逼不得已 想了个馊主意 代码如下
//声明
class THZ
{
private:
struct{UCHAR da[4];}LongRec[65537];
bool FindByR(int);
void BuildList(void);
public:
THZ(){memset(LongRec,0,sizeof(UCHAR)*4*65537);BuildList();}
int FindD(int,int*);
};
//定义
bool THZ::FindByR(int t)
{
return(0!=LongRec[t].da[0]);
}
void THZ::BuildList(void)//慢就慢吧 反正只要在计算过程中快就可以了
{
int i,j[4],m;
for(j[0]=1;j[0]<17;j[0]++)
for(j[1]=1;j[1]<17;j[1]++)
for(j[2]=1;j[2]<17;j[2]++)
for(j[3]=1;j[3]<17;j[3]++)
{
m=j[0]*j[1]*j[2]*j[3];
if(!FindByR(m))
{
for(i=0;i<4;i++)LongRec[m].da[i]=j[i];
}
}
}
int YES(int t){return t=0?1:-1;}
int THZ::FindD(int w,int*a)
{
int i=0,j=0,t=w;
if(t>65536||t<=0||NULL==a)return -1;
Label:
if(LongRec[t].da[0]>0)i=t;
else
{
j++;
t=w+YES(j%2)*(int)(j/2);
if(abs(w-t)>50)return -2;
goto Label;
}
for(int k=0;k<4;k++)a[k]=LongRec[i].da[k];
return 0;
}
//使用示例
THZ*phh=NULL;
void CHZDlg::OnOK()
{
// TODO: Add extra validation here
if(NULL==phh)phh=new THZ;
int a[4],d,b;
d=GetDlgItemInt(IDC_EDIT1);
if(phh->FindD(d,a)==0)
{
b=a[0]*a[1]*a[2]*a[3];
SetDlgItemInt(IDC_EDIT3,a[0],false);
SetDlgItemInt(IDC_EDIT4,a[1],false);
SetDlgItemInt(IDC_EDIT5,a[2],false);
SetDlgItemInt(IDC_EDIT6,a[3],false);
SetDlgItemInt(IDC_EDIT2,b,false);
}
//CDialog::OnOK();
}
还是想请高手指点指点了 谢谢
回复
fireseed 2009-07-30
典型的背包问题,只是把加法换成乘法
回复
acdbxzyw 2009-07-30
最简单的四重循环啦。呵呵。
回复
ckok 2009-07-30
简单地用四重循环当然可以,但是数据规模恐怕难以控制,而使用改造后的背包算法对复杂度的影响也不是特别大,因为我是用在针对设备的实时控制上,所以对效率的要求很高,初步考虑使用合数分解或事先生成数据文件的方式,但还没有特别好的思路,期待有高手能进一步提出意见,谢谢了!
回复
无·法 2009-07-30
帮顶
回复
mbh0210 2009-07-30
也可以使用完美匹配来完成,遍历4中可能,相乘所得的结果A与R做比较,A-R取绝对值C,最小的即为最佳的,等于0直接退出.
回复
丈八涯 2009-07-30
这个应该属于"整数分解"问题.
其实是一道很难的数学题.
回复
绿色夹克衫 2009-07-30
给段简单的代码吧


using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int n = int.Parse(Console.ReadLine());

int i = 0;

while (true)
{
if(factorization(n + i))
{
WriteFactorization(n + i);
return;
}

if (factorization(n - i))
{
WriteFactorization(n - i);
return;
}

i++;
}
}

public static bool factorization(int m)
{
for (int i = 16; i > 1; i--)
{
while (m % i == 0)
m /= i;

if (m <= 16)
return true;
}

return false;
}

public static void WriteFactorization(int m)
{
Console.Write("{0} = ", m);

for (int i = 16; i > 1; i--)
{
while (m % i == 0)
{
m /= i;
Console.Write("{0} ", i);
}

if (m <= 16 && m > 1)
break;
}

Console.Write("{0} ", m);
}
}
}
回复
绿色夹克衫 2009-07-30
虽说可以转化为背包问题,但也要看如何求解,否则效率同4重循环没有区别。但从数据规模上来看,恐怕用背包法很难有大的改进。
我觉得还不如直接向前或向后逐个实验,看该数是否可以写为4个1-16的数的乘机。这样感觉效率还要搞一些。

分解为4个1-16的数用贪心就可以了。
回复
LeonTown 2009-07-30
没看明白。
大侠给讲一下吧。

[Quote=引用 8 楼 litaoye 的回复:]
给段简单的代码吧


C# code
using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int n = int.Parse(Console.ReadLine());

int i = 0;

while (true)
{
if(factorization(n + i))
{
WriteFactorization(n + i);

[/Quote]
回复
showjim 2009-07-30
如果需要频求解的话,积组合9316个元素相对于65536个元素密度还算大,可以把生成一张位图(不计内存的话可以生成乘数组合数组,不用分解值),求解的时候只要找最接近的前驱值和后驱值分解就行了。
如果只是一次性求解,可以生成两个数的积组合136个元素并排序(可以在生成的时候就排好序),然后限定最大值与最小值区间,对于区间内的每个数进行迭代或二分查找最佳匹配值。当然如果R小于17就没必要了。我就说这么多了,也许你还能发现其它规律。
回复
xxjjs 2009-07-30
[Quote=引用 4 楼 xxjjs 的回复:]
质因数分解,如没有再加一或减一继续质因数分解

不过看你的题意:

又如R=28 得到四个数为7,4,1,1

如果数字可以重复,不可能无解,至少x,1,1,1是一组解
[/Quote]

Sorry,没有看到第一行 “四个数都1到16之间的整型”
回复
xxjjs 2009-07-30
质因数分解,如没有再加一或减一继续质因数分解

不过看你的题意:

又如R=28 得到四个数为7,4,1,1

如果数字可以重复,不可能无解,至少x,1,1,1是一组解
回复
jf
回复
发动态
发帖子
数据结构与算法
创建于2007-08-27

3.2w+

社区成员

数据结构与算法相关内容讨论专区
申请成为版主
社区公告
暂无公告