首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

请问一个算法

2012-03-12 
请教一个算法假如A{a1,a2,…,an}定义f(A,x) min{ (x-ai)^2 | ai属于A }假如X{x1,x2,…,xm}是已知样本,要

请教一个算法
假如A={a1,a2,…,an}
定义f(A,x) = min{ (x-ai)^2 | ai属于A }

假如X={x1,x2,…,xm}是已知样本,要求出一个元素个数已知的A使得
  f(A,x1)+f(A,x2)+…+f(A,xm)
最小
请教算法

[解决办法]
ls:
向量A的维数不一定等于向量X的维数。

[解决办法]
不失一般性,设x1<=x2<=x3……<=xm,a1<=a2<=a3……<=an。
当n>=m时,只要,ai=xi(i为1到m)和ai=x1(m<i<n)即可。所以假设m>n。
下面以m=9、n=4为例:
我们把m表示为n个正整数的和叫做m的n剖分。在剖分中数字可以重复,且次序有效,即不同的次序是不同的剖分。
对于某一个剖分,例如对于9=4+2+1+2,我们称a1=mean(x1,x2,x3,x4)、a2=mean(x5,x6)、a3=mean(x7)、a4=mean(x8,x9)为一组预备解(其中mean表示算数平均值)。对于这组解,如果满足x4<=mean(a1,a2)<=x5、x6<=mean(a2,a3)<=x7、x7<=mean(a3,a4)<=x8(我们简称这个条件为边界条件吧),那么,这组解在局部使函数f呈现极小值,我们称之为一组局部解。求出所有的局部解,其中最小的那个就是问题的解了。
按照上面的思路,可以采取下面的步骤:
1、排序x;
2、穷举所有的m的n剖分;
3、对于每一组剖分,求出其预备解,验证所谓边界条件,判断是否为局部解;
4、求出所有局部解对应的函数f的值,其中的最小值为问题的解。

[解决办法]
当n>=m的时候,取X的元素就行了;

当n<m的时候,必定有某个A中的元素(假设为ak)对应有多个X中的元素(假设为Xi,Xj,xi<xj)使得:
f(A,xi)=(xi-ak)^2; f(A,xj)=(xj-ak)^2;
假设有xi<xi'<xj;容易证明f(A,xi')=(xi'-ak)^2;

因为如果存在ak'<>ak,f(A,xi')=(xi'-ak')^2,
如果ak'<ak,那么(xi-ak)^2>(xi-ak')^2,矛盾;
如果ak'>ak,那么(xj-ak)^2>(xj-ak')^2,矛盾;

所以题目相当于对排序后的X分成m组,然后对每组(xi...xj)求一个数,使得sum((xi'-a)^2),i<i'<j最小;

此过程可以用动态规划法来求解;

现在的难点就在于每组(xi...xj)求一个数的解法(相当与X==该小组,A的元素个数为1的原问题);
由方程式min((xi-a)^2+..+(xi'-a)^2+..+(xj-a)^2)
是否有lz说的“如果n等于1那么很容易知道a1等于X的平均值”就不太清楚了;


[解决办法]
5楼效率低下的原因是因为第2步要求穷举所以的剖分。
其实,如果m较大,n较小,那么是不必要的。
最佳解对应的剖分总是使每一个a所对应的区域长度(而非x的个数)大致相等。
因此,第2步中,可以按照这个标准来将x分段,然后在一定范围内调节,就可以了。

最后,调节的范围要看具体的x的分布。
比如说有4个x,按照如下分布:
-x1-x2-----------------------x3--x4-->
而要有剖分为3个段(有3个a),那么a1->{x1},a2->{x2},a3->{x3,x4}和a1->{x1,x2},a2->{x3},a3->{x4}这两组分组方式都对应局部的极值,所以就都要算出比较了。

[解决办法]
可以在o(n^2)求出全部的局部解;
用数组dp1[m][m]来保存全部的局部解;
如果dp1[i+i][j]已知,那么dp1[i][j]=(dp1[i+i][j]*(j-i)+x[i])/(j-i+1)

对所有的局部解搜索最优解:
用数组dp2[8][m][m]来保存中间结果,dp2[8][1][m]是最终的解,那么可以在o(n^3)求出;
dp2[i][j][k]=min(dp1[j][j']+dp2[i-1][j'+1][k]),j<j'<k

还没找到小于N^3的搜索最优解的方法;
不过o(n^3)对10^5的数量级应该也可行的吧



[解决办法]
当n<m时:对X元素进行排序后为:X1,X2,X3,....,Xm
1)先证明I个元素的集合,使得F=(x-a1)^2+(x-a2)^2+...+(x-aI)^2最小值的数X是I所有元素的平均值
F=x^2-2xa1+a1^2+....
 =I*x^2-2x(a1+a2+a3...+aI)+a1^2+a2^2+...aI^2
 可以得出F最小值的解,x=(a1+a2+a3...+aI)/I;
2)将集合X从小到大划分成s组集合,其中1<=s<=n;这时可以看出跟矩阵连乘的问题一样,可以用动态规划法来解;


3)其实可以证明s==n时得到的是最优的(证明略),所以当用动态规划求解时只要计算到(m-n)个元素,不用计算到n;

[解决办法]
搜索最优解用dp2[n][m]来保存最优解,dp2[i][j]表示从xj-xm分成i组的最优解,那么最后最优解就是dp2[n][1];

dp2[i][j]=min(dp1[j][j']+dp2[i-1][j'+1]) j<j'<m

这样复杂度就降低成了o(m*n);


[解决办法]
to:IT_worker 
复杂度不是这么计算的:

动态规划的推导公式是:
dp2[i][j]=min(dp1[j][j ']+dp2[i-1][j '+1]) j <j ' <m 

计算dp2[n][m]的单个元素的复杂度为O(m);
元素总个数为n*m个;

所以总的复杂度为O(n*m^2);
又因为"n的值大约是8",可以忽略掉n;
[解决办法]
如果不是必须求最优值,可以考虑使用模式识别中的聚类分析。将m个数据先聚类成n类。
然后每类求个平均值,基本上就差不多了。
[解决办法]
参考这个:


http://topic.csdn.net/u/20071016/16/6734119d-27dd-4efd-91ea-a9290d6545c0.html
终于搞清楚了C#二进制的一些关键操作了,解决了微软面试题,求数组中两两之差绝对值最小的值O(N)最少内存限制的问题! 

研究了好几天,写出来一个看起来象O(n)的算法,O(nlog)就不用写了. 
思路是桶排序O(N)复杂度,为了节省空间,采用了Byte[]数组,1个byte代表8个数字的有无. 
回头整理公布一下C#二进制运算的一些重要操作. 

C# code
using System;namespace MinABS{    class Program    {        static void Main(string[] args)        {            int n = 100;            int[] a = new int[n];            Random rand = new Random();            int min = int.MaxValue;            int max = int.MinValue;            Console.WriteLine("产生了" + n + "个数据的实验数组,");            Console.WriteLine("本程序适用于int.MinValue到int.MaxValue范围,请自行修改输入的量和范围");            for (int i = 0; i < n; i++)            {                //赋值并且取到最大最小值                //a[i] = rand.Next(int.MinValue, int.MaxValue);                 a[i] = rand.Next(-100, 100);                if (a[i] < min) { min = a[i]; }                if (a[i] > max) { max = a[i]; }                 Console.Write(a[i] + " ");                            }             Console.WriteLine();            Console.WriteLine("在O(n)内得到最大最小分别是:");            Console.WriteLine(max + "和" + min);                        long offset = (long)max + Math.Abs((long)min);            //规划数组的长度。每个byte有8位长            int len = (int)(offset >> 3) +1 ;            Byte[] B = new Byte[len];            int kkkk = 0;            bool IsSame = false;//是否有重合点标记                       //O(n)的时间内分配到了Byte[]中。                        for (int i = 0; i < n; i++)            {                offset = (long)a[i] - (long)min;                int index = (int)(offset >> 3);                int temp = B[index];                //把末k位变成1                //把右数第k位变成1 例如:(00101001->00101101,k=3)     x | (1 << (k-1))                             int tempOffSet = (1 << (  (int)(offset & 7) ) );                //判断重合                if (!IsSame)                {                    kkkk = temp & tempOffSet;                    if ((temp & tempOffSet) >= 1)                    {                        IsSame = true;                        //如果0算最小距离就在这里退出吧。本代码判断重合,但没把0作为结果。                                            }                }                int bbb = B[index];                                B[index]  |=  (Byte)(tempOffSet);                          int aaa = B[index];              }            //最小距离初始为最大。            Console.WriteLine("在O(n)的时间内分配到了Byte[]中,正在计算最小距离,请稍候。。。。。");            long minABS = long.MaxValue;            long lastIndex = -1;            //在常数范围内循环,复杂度不增加。最坏的情况是32*int.MaxValue次。            for (int i = 0; i < B.Length; i++)            {                //if (B[i] == 0) { continue; }                //在常数范围内循环,复杂度不增加。                for (int k = 0; k < 8; k++)                {                    if (((B[i] >> k) & 1) == 1)                    {                        if (lastIndex >= 0)                        {                            long temp = ((long)i << 3) + k - lastIndex;                            if (temp < minABS)                            {                                minABS = temp;                               Console.WriteLine("目前得到了最小距离:" + minABS);                            }                        }                        lastIndex = (i << 3) + k;                                            }                }            }            if (IsSame)            { Console.WriteLine("有重合点"); }            else            { Console.WriteLine("无重合点"); }            Console.WriteLine("不考虑重合最小距离是:" + minABS);            Console.WriteLine("总复杂度是:O(n)");                        Console.ReadLine();        }    }}
[解决办法]


问题可以理解为:
m,i
min∑(|xi|-|aj|)
i,j
 
"最小线段合最小"的问题。
首先,发现题目存在问题: 
f(A,x1)+f(A,x2)+…+f(A,xm) 本身就是常数,不存在最小合的问题。

如果只为了求出这个数字

第一步,求出最小线段:

aj和xi的关系是“最小线段”的关系:

点到集合最小距离,分2种情况
1,xi点在集合A={a1,a2,…,an}内,要遍历集合取到最小值,
复杂度是:
对A集合桶排序是的时间 + 二分查找m次xi的时间
=O(n) + O(mlogn)
=O(n+mlogn)
如果A集合分散度过高,采用AVL二叉树排序,总复杂度是O(nlogn)+O(mlogn)=O((n+m)logn)
2,xi点全在集合A={a1,a2,…,an}外,取集合最大/小 ,相减就可以了。总复杂度是O(m)。

故第一步复杂度应该是O(m)到O(n+mlogn)或者O((n+m)logn),取决于两个集合范围和离散程度。

第二步,求最小合后:

最小线段求出后,问题就变成了
m m
min∑(|xi|-Ki)=min∑|yi| 
i i
因为其中Ki为常量集合,所以在O(m)内演变成Y ={y1,y2,…,ym} 同样是已知样本。
求其合就可以了。

两步总的复杂度是
O(n+m+mlogn)
或者O(m+(n+m)logn)
根据离散情况选择最小的做法。

热点排行