找回密码
 欢迎注册
楼主: 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. bigNum= (n1>n2)?n1:n2;
  18. smallNum=(n1<n2)?n1:n2;
  19. while (1)
  20. {
  21. modNum=bigNum % smallNum;
  22. if (modNum==0)
  23. break;
  24. bigNum=smallNum;
  25. smallNum=modNum;
  26. }
  27. return smallNum;
  28. }
  29. void divideIntArray(DWORD n)
  30. {
  31. BYTE* pBuff=NULL;
  32. DWORD lcm,gcd,limit;
  33. DWORD i,j;
  34. BOOL first;
  35. int count;
  36. pBuff=new BYTE[n+2];
  37. if (pBuff==NULL)
  38. return;
  39. pBuff[0]=pBuff[1]=0;
  40. for (i=2;i<=n;i++)
  41. pBuff[i]=1;
  42. j=0;
  43. while (TRUE)
  44. {
  45. i=2;
  46. lcm=1;
  47. limit=MAX_DWORD/lcm;
  48. while (TRUE)
  49. {
  50. DWORD t;
  51. while ( pBuff[i]==0 && i<=n) //skip empty number
  52. i++;
  53. if (i>n)
  54. break;
  55. gcd=GCD32(lcm,i);
  56. t=i / gcd;
  57. if (t < limit && t>1)
  58. {
  59. lcm=lcm * t;
  60. limit=MAX_DWORD/lcm;
  61. }
  62. if (lcm > MAX_DWORD/2)
  63. break;
  64. i++;
  65. }
  66. if (lcm==1 )
  67. break;
  68. printf("\nB[%d]=",j);
  69. for (count=0,first=TRUE,i=2;i<=n;i++)
  70. {
  71. if ( pBuff[i] && lcm % i==0)
  72. {
  73. if (!first)
  74. printf(",");
  75. printf("%u",i);
  76. pBuff[i]=0;
  77. first=FALSE;
  78. count++;
  79. }
  80. }
  81. printf("\n");
  82. printf("Total %d number in B[%d], LCM of B[%d] =%u\n",count,j,j,lcm);
  83. j++;
  84. }
  85. delete[] pBuff;
  86. pBuff=NULL;
  87. }
  88. void divideIntArray(DWORD n);
  89. int main(int argc, char* argv[])
  90. {
  91. divideIntArray(100);
  92. return 0;
  93. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 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. bigNum= (n1>n2)?n1:n2;
  19. smallNum=(n1<n2)?n1:n2;
  20. while (1)
  21. {
  22. modNum=bigNum % smallNum;
  23. if (modNum==0)
  24. break;
  25. bigNum=smallNum;
  26. smallNum=modNum;
  27. }
  28. return smallNum;
  29. }
  30. void divideIntArray(DWORD n)
  31. {
  32. BYTE* pBuff=NULL;
  33. DWORD lcm,gcd,limit;
  34. DWORD i,j;
  35. BOOL first;
  36. int count;
  37. pBuff=new BYTE[n+1];
  38. if (pBuff==NULL)
  39. return;
  40. memset(pBuff, 1, n+1);
  41. // memset(pBuff+1, 0, n/2); //13# 的简化算法
  42. j=0;
  43. while (TRUE)
  44. {
  45. i=n+1;
  46. lcm=1;
  47. limit=MAX_DWORD/lcm;
  48. while (TRUE)
  49. {
  50. DWORD t;
  51. while (pBuff[--i]==0) //skip empty number
  52. ;
  53. if (0 == i)
  54. break;
  55. gcd=GCD32(lcm,i);
  56. t=i / gcd;
  57. if (t < limit && t>1)
  58. {
  59. lcm=lcm * t;
  60. limit=MAX_DWORD/lcm;
  61. }
  62. if (lcm > MAX_DWORD/2)
  63. break;
  64. }
  65. if (lcm==1 )
  66. break;
  67. printf("\nB[%d]=",j);
  68. for (count=0,first=TRUE,i=1;i<=n;i++)
  69. {
  70. if ( pBuff[i] && lcm % i==0)
  71. {
  72. if (!first)
  73. printf(",");
  74. printf("%u",i);
  75. pBuff[i]=0;
  76. first=FALSE;
  77. count++;
  78. }
  79. }
  80. printf("\n");
  81. printf("Total %d number in B[%d], LCM of B[%d] =%u\n",count,j,j,lcm);
  82. j++;
  83. }
  84. delete[] pBuff;
  85. pBuff=NULL;
  86. }
  87. void divideIntArray(DWORD n);
  88. int main(int argc, char* argv[])
  89. {
  90. divideIntArray(1024);
  91. return 0;
  92. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 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, 2025-3-9 10:41 , Processed in 0.052729 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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