找回密码
 欢迎注册
楼主: liangbch

[讨论] 数组拆分算法

[复制链接]
 楼主| 发表于 2009-1-8 10:09:41 | 显示全部楼层
1. 我实现了8#中提到的第一个算法,先给出运行结果

  1. B[0]=2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,24,25,26,28,30,32,33,34,35,36,38,39,40,42,44,45,48,50,51,52,55,56,57,60,63,65,66,68,70,72,75,76,77,78,80,84,85,88,90,91,95,96,99,100
  2. Total 65 number in B[0], LCM of B[0] =2327925600

  3. B[1]=23,27,29,31,37,41,46,54,58,62,69,74,82,87,93
  4. Total 15 number in B[1], LCM of B[1] =1693818486

  5. B[2]=43,47,49,53,59,86,94,98
  6. Total 8 number in B[2], LCM of B[2] =619327366

  7. B[3]=61,64,67,71,73
  8. Total 5 number in B[3], LCM of B[3] =1355706944

  9. B[4]=79,81,83,89
  10. Total 4 number in B[4], LCM of B[4] =47269413

  11. B[5]=92,97
  12. Total 2 number in B[5], LCM of B[5] =8924
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-1-8 10:12:59 | 显示全部楼层
这里给出代码:
  1. #include "stdio.h"
  2. #include "stdlib.h"

  3. typedef unsigned long DWORD;
  4. typedef unsigned char BYTE;
  5. typedef unsigned BOOL;
  6. #define TRUE 1
  7. #define FALSE 0


  8. #define MAX_DWORD (4294967295L)

  9. DWORD GCD32(DWORD n1, DWORD n2) //Greatest Common Divisor
  10. // 功能: 求n1,n2的最大公约数
  11. //***************************************************
  12. {
  13.         DWORD bigNum,smallNum,modNum;
  14.         //----------------------------------------
  15.         if (n1==n2)
  16.                 return n1;
  17.        
  18.         bigNum=  (n1>n2)?n1:n2;
  19.         smallNum=(n1<n2)?n1:n2;
  20.        
  21.         while (1)
  22.         {
  23.                 modNum=bigNum % smallNum;
  24.                 if (modNum==0)
  25.                         break;
  26.                 bigNum=smallNum;
  27.                 smallNum=modNum;
  28.         }
  29.         return smallNum;
  30. }

  31. void divideIntArray(DWORD n)
  32. {
  33.         BYTE* pBuff=NULL;
  34.         DWORD lcm,gcd,limit;
  35.         DWORD  i,j;
  36.         BOOL  first;
  37.         int    count;

  38.         pBuff=new BYTE[n+2];
  39.         if (pBuff==NULL)
  40.                 return;
  41.        
  42.         pBuff[0]=pBuff[1]=0;
  43.         for (i=2;i<=n;i++)
  44.                 pBuff[i]=1;
  45.        
  46.         j=0;
  47.         while (TRUE)
  48.         {
  49.                 i=2;  
  50.                 lcm=1;
  51.                 limit=MAX_DWORD/lcm;
  52.                 while (TRUE)
  53.                 {
  54.                         DWORD t;
  55.                         while ( pBuff[i]==0 && i<=n) //skip empty number
  56.                                 i++;
  57.                         if (i>n)
  58.                                 break;
  59.                         gcd=GCD32(lcm,i);
  60.                        
  61.                         t=i / gcd;
  62.                         if (t < limit && t>1)
  63.                         {
  64.                                 lcm=lcm * t;
  65.                                 limit=MAX_DWORD/lcm;
  66.                         }
  67.                         if (lcm > MAX_DWORD/2)
  68.                                 break;
  69.                         i++;
  70.                 }
  71.                
  72.                 if (lcm==1 )
  73.                         break;
  74.                 printf("\nB[%d]=",j);
  75.                 for (count=0,first=TRUE,i=2;i<=n;i++)
  76.                 {
  77.                         if ( pBuff[i] && lcm % i==0)
  78.                         {
  79.                                 if (!first)
  80.                                         printf(",");
  81.                                 printf("%u",i);
  82.                                 pBuff[i]=0;
  83.                                 first=FALSE;
  84.                                 count++;
  85.                         }
  86.                 }
  87.                 printf("\n");
  88.                 printf("Total %d number in B[%d], LCM of B[%d] =%u\n",count,j,j,lcm);
  89.                 j++;
  90.         }
  91.         delete[] pBuff;
  92.         pBuff=NULL;
  93. }


  94. void divideIntArray(DWORD n);
  95. int main(int argc, char* argv[])
  96. {
  97.         divideIntArray(100);
  98.         return 0;
  99. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-1-8 10:28:52 | 显示全部楼层
突然想到一个比较好的简化思路:

1、将数组倒序排序,记作数组A;

2、从A中取出第一个数及其所有的约数,形成一个新数组;

3、重复步骤2,直至A为空为止。

此时将原数组初次划分成一系列的新数组,
这些新数组的“排头兵”可以完全屏蔽其后数字的“最小公倍数”。
所以只要将这些“排头兵”进一步按楼主的要求进行划分即可。

如果对前n个连续正整数进行上述操作,最终将有且仅有[(n+1)/2]组,
它们的“排头兵”依次为:n, n-1, ..., [n/2]+1
这样可以将工作量下降50%
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-1-8 10:44:58 | 显示全部楼层
但不知这么一来,生成的结果是否比直接处理更优。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-1-8 10:57:03 | 显示全部楼层
是完全等同的。

因为这些“排头兵”也是必须进行划分的,
它们各自的约数就象小第跟着老大站好队伍即可。
一个小弟可以有多个“老大”可选,任选一个即可。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-1-8 11:03:28 | 显示全部楼层
你的12#程序计算 divideIntArray(1024)  得到104 组划分。

我将它略作改造,仅将顺序由升序变为降序,得到的结果则仅需86组。

代码如下:
  1. #include "stdio.h"
  2. #include "stdlib.h"
  3. #include <memory.h>

  4. typedef unsigned long DWORD;
  5. typedef unsigned char BYTE;
  6. typedef unsigned BOOL;
  7. #define TRUE 1
  8. #define FALSE 0


  9. #define MAX_DWORD (4294967295L)

  10. DWORD GCD32(DWORD n1, DWORD n2) //Greatest Common Divisor
  11. // 功能: 求n1,n2的最大公约数
  12. //***************************************************
  13. {
  14.         DWORD bigNum,smallNum,modNum;
  15.         //----------------------------------------
  16.         if (n1==n2)
  17.                 return n1;
  18.        
  19.         bigNum=  (n1>n2)?n1:n2;
  20.         smallNum=(n1<n2)?n1:n2;
  21.        
  22.         while (1)
  23.         {
  24.                 modNum=bigNum % smallNum;
  25.                 if (modNum==0)
  26.                         break;
  27.                 bigNum=smallNum;
  28.                 smallNum=modNum;
  29.         }
  30.         return smallNum;
  31. }

  32. void divideIntArray(DWORD n)
  33. {
  34.         BYTE* pBuff=NULL;
  35.         DWORD lcm,gcd,limit;
  36.         DWORD  i,j;
  37.         BOOL  first;
  38.         int    count;
  39.        
  40.         pBuff=new BYTE[n+1];
  41.         if (pBuff==NULL)
  42.                 return;
  43.        
  44.         memset(pBuff, 1, n+1);
  45. //        memset(pBuff+1, 0, n/2); //13# 的简化算法
  46.        
  47.         j=0;
  48.         while (TRUE)
  49.         {
  50.                 i=n+1;  
  51.                 lcm=1;
  52.                 limit=MAX_DWORD/lcm;
  53.                 while (TRUE)
  54.                 {
  55.                         DWORD t;
  56.                         while (pBuff[--i]==0) //skip empty number
  57.                                 ;
  58.                         if (0 == i)
  59.                                 break;
  60.                         gcd=GCD32(lcm,i);
  61.                        
  62.                         t=i / gcd;
  63.                         if (t < limit && t>1)
  64.                         {
  65.                                 lcm=lcm * t;
  66.                                 limit=MAX_DWORD/lcm;
  67.                         }
  68.                         if (lcm > MAX_DWORD/2)
  69.                                 break;
  70.                 }
  71.                
  72.                 if (lcm==1 )
  73.                         break;
  74.                 printf("\nB[%d]=",j);
  75.                 for (count=0,first=TRUE,i=1;i<=n;i++)
  76.                 {
  77.                         if ( pBuff[i] && lcm % i==0)
  78.                         {
  79.                                 if (!first)
  80.                                         printf(",");
  81.                                 printf("%u",i);
  82.                                 pBuff[i]=0;
  83.                                 first=FALSE;
  84.                                 count++;
  85.                         }
  86.                 }
  87.                 printf("\n");
  88.                 printf("Total %d number in B[%d], LCM of B[%d] =%u\n",count,j,j,lcm);
  89.                 j++;
  90.         }
  91.         delete[] pBuff;
  92.         pBuff=NULL;
  93. }


  94. void divideIntArray(DWORD n);
  95. int main(int argc, char* argv[])
  96. {
  97.         divideIntArray(1024);
  98.         return 0;
  99. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-1-8 11:05:56 | 显示全部楼层
谢谢楼上,好快的编程速度!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-1-8 11:08:11 | 显示全部楼层
我只是略作改造(不敢说“改进”)而已,
主要模块还是你的。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-1-8 15:19:51 | 显示全部楼层
你改进的这个版本得到的数组的个数确实少了。
我提的这个问题的和求高精度对数有关。其实精确的需求是这样的。
   将包含1..n共n个自然数的数组拆成几个数组,
     1)使得得到的每个数组各个数的最小公倍数小于2^32
            2)使得得到的所有数组的跨度的和最小。跨度是这样定义的。如一个数组中最小的那个数是x。则这个数组的跨度L=n-x+1

       1.有时间验证下,你改进的程序和我的原始版本,那个跨度和最小。
   2.我将试验我在8#提到的第二个算法,估计,这个算法得到的结果应该比现在的这个版本得到的结果更优。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-1-9 13:40:16 | 显示全部楼层
重新描述一下我的第2个算法。

定义:
  1.符号$p_n$ 表示第n个质数,如$p_1$=2,$p_2=3$,$p_3=5$
    2.自然数的阶,如果一个自然数的最大素因数是$p_k$,我们说这个自然数的阶是k. 例:1的阶是0,2的阶是1,60的阶是3,7的阶是4.

数组拆分算法:
  1.将1到n之间的所有自然数按照其阶和它的大小排序,即,首先阶从小到大排序,阶相同者按照数的从小到大的顺序排序,另排序后的数组为A。
  2.置i=1;   
    3.从数组A中顺序取数,直到这几个数的最小公倍数lcm恰好小于2^32.
    4.将所有lcm的约数从数组中移出,放入$B_i$数组.
    5.如果数组A为空,结束。
  6.i=i+1,转3.
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-5-3 14:18 , Processed in 0.042917 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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