无心人 发表于 2008-8-26 20:23:41

不是吧
只要正向得到
形式如2pq + 1 类型素数就可以了吧

sunwukong 发表于 2008-9-13 16:45:50

对 2048 以内的素数分组 有 92 组的结果:

No. 1 :    2039 2029 1033
No. 2 :    2027 2017 1049
No. 3 :    2011 2003 1063
No. 4 :    1999 1997 1069
No. 5 :    1993 1987 1061
No. 6 :    1979 1973 1097
No. 7 :    1951 1949 1129
No. 8 :    1933 1931 1123
No. 9 :    1913 1907 1171
No. 10 :    1901 1889 1193
No. 11 :    1879 1877 1217
No. 12 :    1873 1871 1223
No. 13 :    1867 1861 1231
No. 14 :    1847 1831 1259
No. 15 :    1823 1811 1297
No. 16 :    1801 1789 1327
No. 17 :    1787 1783 1321
No. 18 :    1777 1759 1373
No. 19 :    1753 1747 1399
No. 20 :    1741 1733 1423
No. 21 :    1723 1721 1447
No. 22 :    1709 1699 1471
No. 23 :    1697 1693 1493
No. 24 :    1669 1667 1543
No. 25 :    1663 1657 1553
No. 26 :    1637 1627 1609
No. 27 :    1621 1619 1613
No. 28 :    1607 1601 1597
No. 29 :    1583 1579 1571
No. 30 :    1567 1559 1549
No. 31 :    1531 1523 1511
No. 32 :    1499 1489 1487
No. 33 :    1483 1481 1459
No. 34 :    1453 1451 1439
No. 35 :    1433 1429 1427
No. 36 :    1409 1381 1367
No. 37 :    1361 1319 1307
No. 38 :    1303 1301 1291
No. 39 :    1289 1283 1279 2
No. 40 :    1277 1249 1237
No. 41 :    1229 1213 1201
No. 42 :    1187 1181 1163
No. 43 :    1153 1151 1117
No. 44 :    1109 1103 1093 3
No. 45 :    1091 1087 1051
No. 46 :    1039 1031 1021
No. 47 :    1019 1013 1009
No. 48 :    997 991 983
No. 49 :    977 971 967
No. 50 :    953 947 941 5
No. 51 :    937 929 919
No. 52 :    911 907 887
No. 53 :    883 881 877
No. 54 :    863 859 857
No. 55 :    853 839 829 7
No. 56 :    827 823 821
No. 57 :    811 809 797
No. 58 :    787 773 769
No. 59 :    761 757 751
No. 60 :    743 739 733
No. 61 :    727 719 709 11
No. 62 :    701 691 683
No. 63 :    677 673 661 13
No. 64 :    659 653 647
No. 65 :    643 641 631
No. 66 :    619 617 613 17
No. 67 :    607 601 599 19
No. 68 :    593 587 577
No. 69 :    571 569 563 23
No. 70 :    557 547 541
No. 71 :    523 521 509 29
No. 72 :    503 499 491 31
No. 73 :    487 479 467 37
No. 74 :    463 461 457 43
No. 75 :    449 443 439 47
No. 76 :    433 431 421 53
No. 77 :    419 409 401 61
No. 78 :    397 389 383 71
No. 79 :    379 373 367 79
No. 80 :    359 353 349 97
No. 81 :    347 337 331 109
No. 82 :    317 313 311 139
No. 83 :    307 293 283 167
No. 84 :    281 277 271 199
No. 85 :    269 263 257 233
No. 86 :    251 241 239 229
No. 87 :    227 223 211 197
No. 88 :    193 191 181 179
No. 89 :    173 163 157 151
No. 90 :    149 137 131 127
No. 91 :    113 107 103 83 41
No. 92 :    101 89 73 67 59

gxqcn 发表于 2008-9-13 16:57:58

楼上说说,你是如何做到的?

这个悬赏至今刚好满一个月了,
之前一直没有更好的突破,
所以,打算继续悬赏一个月,

对于有突破的算法或结论,
除了可以得到悬赏的100枚金币,
还可获得本人私下转让的金币(数目不菲的)。

sunwukong 发表于 2008-9-13 18:16:38

#include < iostream.h >
#include < math.h >

#include "D:/HugeCalc/HugeCalc.h"// 公共接口
#include "D:/HugeCalc/HugeInt.h"   // 10进制系统
#include "D:/HugeCalc/HugeIntX.h"// 16进制系统

#pragma message( "automatic link to D:/HugeCalc/HugeCalc.lib" )
#pragma comment( lib, "D:/HugeCalc/HugeCalc.lib" )

const unsigned __int16 Pri1024 = 172; // 1024 以内的素数个数
const unsigned __int16 Pri2048 = 309; // 2048 以内的素数个数

const __int64 BOUND32 = (1ul << 32) - 1;
const __int64 BOUND14 = (1ul << 14) - 1;
const __int64 BOUND10 = (1ul << 10) - 1;

typedef CHugeIntX integer;

unsigned __int16 arr[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039 };

//const unsigned __int32 ProblemSize = 1023;
//unsigned __int16 arr;

__int32 MinimumGroup(const unsigned __int16* const arr, const unsigned __int16 _SIZE, const __int64 _BOUND);

int main(/*int argc, char* argv[]*/ void)
{
        __int32 result;
       
        //for ( __int16 i=0; i<ProblemSize; i++) arr[ i ] = i + 2;
        cout << "Call " << HugeCalc::GetVer() << endl;
       
    HugeCalc::EnableTimer();
    HugeCalc::ResetTimer(0, TIMER_UNIT_ms);
       
        //result = MinimumGroup(arr, ProblemSize, BOUND32);
        //result = MinimumGroup(arr, 25, BOUND14); // 贪婪法=10,最优=9
        //result = MinimumGroup(arr, 36, 22500ul); // 贪婪法=15,最优=14
        //result = MinimumGroup(arr, 72, 8100ul); // 贪婪法=最优=49
        //result = MinimumGroup(arr, Pri1024, BOUND10); // 贪婪法=最优=162
        //result = MinimumGroup(arr, Pri1024, BOUND10*29); // 贪婪法=最优=133
        result = MinimumGroup(arr, Pri1024, BOUND32);
        //result = MinimumGroup(arr, Pri2048, BOUND32);

        HugeCalc::EnableTimer( FALSE );
       
        cout << endl << "Best result = " << result << endl;
       
        cout << endl << HugeCalc::GetTimerStr() << endl;
       
        system( "pause" );
       
    return 0;
}

// 函数名:   MinimumGroup
// 函数功能:   求出把一组正整数 arr ~ arr 划分成最少的组数的方法,使得每组里所有数之积均不大于 _BOUND
//
// 输入参数:
//   arr:    unsigned __int16,数组指针,要求 arr[] 是升序排列
//   _SIZE:unsigned __int16,数组的大小
//   _BOUND: __int64,分组的乘积最大值
//
// 输出参数:   __int32,最小分组数
//            结果为0: _SIZE = 0
//            结果为 -1: _BOUND < arr
//            结果为 -2: arr 不是升序排列
//
// 版本 :      1.2.5.9
// 修改时间:   2008-9-11 00:30
//
// 存在问题:   运算效率很低,限界条件不够精细
// 下一版本改进措施;   乘法变加法,把 arr[]、_BOUND 都取对数,然后扩大某个倍数,化成整数加减运算

__int32 MinimumGroup(const unsigned __int16* const arr, const unsigned __int16 _SIZE, const __int64 _BOUND)
{
        __int32 i, j, k, icur, g, gMin, gMax, gCur, top, bottom, top0, bottom1, top1, Lpro, numOfBig, Lpromin;
        __int32 *bUsed, *pbuf;
       
    __int64 product, maxV, minGV, *pmaxV, *pminGV, *ppro;
       
        top0 = _SIZE - 1;
       
        if ( 0 == _SIZE ) return (0);
        if ( _BOUND < arr ) return (-1);
       
        for ( i=0; i<top0; ++i )
        {
                if ( arr > arr )
                        return (-2);
        }
       
        if ( 0 == arr || 1 == arr ) return (1);       
       
        for ( i=0 ;1 == arr ;++i ); // 1不参加运算
       
        if ( i == top0 ) return (1);
       
        bottom1 = i;
       
        for (maxV=_BOUND/arr, i=top0 ; i>bottom1 && arr>maxV ; --i ); // 只能单独做一组的数
       
        if ( i - bottom1 < 2 ) return (_SIZE - i);
       
        top1 = i;
       
        integer hollpro(arr), grpV(_BOUND), b(_BOUND);
       
        for ( j=1, i=1+bottom1 ;i<=top1 ;++i )
        {
                hollpro *= arr;
                if ( hollpro > grpV )
                {
                        grpV *= b;
                        ++j;
                }
        }
        grpV = 0;
       
        numOfBig = top0 - top1;
        gMin = j + numOfBig;
        if ( j == 1 ) return (gMin);
       
        // 用贪婪法求分组数的一个上界 gMax,此方法来自 gxqcn
        bUsed = new __int32 ;
       
        for ( i=0; i<_SIZE; ++i ) bUsed = 0;
       
        cout << endl;
       
    for ( gMax=0, bottom=0, top=top0 ;bottom != top ; )
    {
      ++gMax;
                cout << "No. " << gMax << " :   ";
               
                //for ( product=1, maxV=_BOUND, i=top ; i >= bottom;--i )
      for ( maxV=_BOUND, i=top ; i >= bottom;--i )
      {
            if ( !bUsed && arr <= maxV )
            {
                                cout << " " << arr;
                //maxV = _BOUND / ( product *= arr );
                                maxV /= arr;
                bUsed = 1;
            }
      }
                //cout << "      " << product;
                cout << endl;
               
      while ( bottom != top && bUsed ) --top;
      while ( bottom != top && bUsed ) ++bottom;
    }
    if ( !bUsed )
        {
                ++gMax;
                //cout << "No. " << gMax << " :    " << arr << "      " << arr << endl;
                cout << "No. " << gMax << " :    " << arr << endl;
        }
       
        if ( gMax == gMin )
        {
                delete [] bUsed;
                return (gMax); // 上界 = 下界
        }
       
        // 尝试组数更小的方案
       
        // 求第一组的数字的个数
    for ( Lpromin=0, maxV=_BOUND, i=top1 ; i >= bottom1;--i )
    {
      if ( arr <= maxV )
      {
                        maxV /= arr;
                        ++Lpromin;
      }
    }
       
        k = gMax - numOfBig;
       
        for ( maxV=_BOUND, i=bottom1; arr<=maxV; ++i ) maxV /= arr ;
       
        Lpro = i - bottom1 + 1; // 最长乘式的因子数+1
        j = Lpro * gMax;
        pbuf = new __int32 ;       // 所有分组因子下标表格
    pmaxV = new __int64 ;      // 同组的下一个乘积因子允许的最大值
        ppro = new __int64 ;       // 本组前几个因子的乘积
    pminGV = new __int64 ;// 每一组乘积允许的最小值
       
        integer *pGV, *pB, q, r, c;
       
    pGV = new integer ;   // 本组之后未分配的数的乘积
    pB = new integer ;         // 界限参数(_BOUND的乘幂)
       
        for ( i=0; i<j; ++i ) pbuf = 0;
        for ( i=0; i<j; ++i ) pmaxV = 0;
        for ( i=0; i<j; ++i ) ppro = 0;

        for ( top=top0, i=1; i<=numOfBig ; ++i, --top )
        {
                pbuf = 1;
                pbuf = top;
               
                ppro = 1;
                ppro = arr;
               
                bUsed = 1;
        }       
       
        icur = Lpro-2+bottom1;
        if ( Lpromin <= 2 )
        {
                for ( i=icur; i<top1; ++i ) pmaxV = _BOUND - ( _BOUND % arr );
        }
        else
        {
                for ( i=icur; i<top1; ++i )
                {
                        maxV = _BOUND / arr;
                        g = __int32(maxV % arr);
                        for ( j=bottom1+1; g>0 && j<i; ++j)
                        {
                                gCur = __int32(maxV % arr);
                                if ( g > gCur )
                                        g = gCur;
                        }
                        pmaxV = ( maxV - g ) * arr;
                }
        }
       
        // 选择排序
        for ( i=icur; i<icur+k-1; ++i )
        {
                for ( g=i, maxV=pmaxV, j=i+1; j<top1; ++j )
                {
                        if ( maxV < pmaxV )
                        {
                                maxV = pmaxV;
                                g = j;
                        }
                }
                if ( g != i )
                {
                        pmaxV = pmaxV;
                        pmaxV= maxV;
                }
        }
       
        for ( pB=1, pB=pmaxV, j=icur, i=2; i<k; ++i )
        {
                ++j;
                pB = pB * ( q = pmaxV );
        }
        //for ( pB=1, pB=b, i=2; i<k; ++i ) pB = pB * b;       
        b = 0;
       
        for ( i=numOfBig+1; i<gMax; ++i ) pmaxV= _BOUND;
       
        pGV = hollpro;       
       
        for ( gCur=gMax-1 ;gCur >= gMin ;--gCur ) // 组数循环
        {
                for ( top=top1, i=bottom=bottom1; i<=top; ++i ) bUsed = 0;
               
                for ( g=numOfBig+1 ; g<gCur ;) // g 循环
                {
                        q.Div( pGV, pB, &r );
                        if ( !(!r) ) ++q;
                        pminGV = minGV = q;
                       
                        k = 1;
                        pbuf = top;
                        ppro = product = arr;
                        pmaxV = maxV = _BOUND / product;
                        bUsed = 1;
                        icur = --top;
                       
                        // 生成第 g 组乘积
                        do {
                                for ( i=icur; i>=bottom; --i )
                                {
                                        if ( !bUsed && arr <= maxV )
                                        {
                                                ++k;
                                                pbuf = i;
                                                ppro = product *= arr;
                                                pmaxV = maxV /= arr ;
                                                bUsed = 1;
                                        }
                                }
                               
                                if ( product >= minGV ) // 进入下一组
                                {
                                        pbuf = k;
                                        pGV = pGV / (c = product);
                                       
                                        while ( bottom != top && bUsed ) --top;
                                        while ( bottom != top && bUsed ) ++bottom;                                       
                                       
                                        ++g;
                                        break;
                                }

                                // 生成下一个乘积
                                do {
                                        j = bottom;
                                       
                                        do {
                                                i = pbuf;
                                                bUsed = 0;
                                                --k;
                                               
                                                if ( i > top )
                                                {
                                                        top = i;
                                                }
                                                else if ( i < bottom )
                                                {
                                                        bottom = i;
                                                }                                       
                                        } while ( k >= 1 && i < j );
                                       
                                        if ( k >= 1 )
                                        {
                                                icur = --i;
                                                maxV = pmaxV;
                                                product = ppro;
                                                break;
                                        }                                       
                                       
                                        --g; // 退回到上一组
                                       
                                        if ( numOfBig == g ) // 退到底了
                                        {
                                                delete [] pB;
                                                delete [] pGV;
                                                delete [] pminGV;
                                                delete [] ppro;
                                                delete [] pmaxV;
                                                delete [] pbuf;
                                                delete [] bUsed;
                                               
                                                return (gCur+1);
                                        }
                                       
                                        k = pbuf;
                                        minGV = pminGV;
                                       
                                } while ( 1 ); // end of 生成下一个乘积                               
                               
                        } while ( 1 ); // end of 生成第 g 组乘积
                       
                } // end of g 循环
               
                for ( j=0, i=top; i>=bottom; --i) // 最后一组
                {
                        if ( !bUsed )
                        {
                                ++j;
                                pbuf = i;
                        }
                }
                pbuf = j;
               
                // 输出
                cout << endl << "groups = " << gCur << endl;
                for ( i=1 ;i <= gCur ;++i )
                {
                        cout << "No. " << i << " :   ";
                       
                        for ( k=pbuf, j=1 ;j <= k ;++j )
                                cout << " " << arr[ pbuf ];
                               
                        cout << endl;
                }
                cout << endl;
               
        } // end of 组数循环
       
        delete [] pB;
        delete [] pGV;
        delete [] pminGV;
        delete [] ppro;
        delete [] pmaxV;
        delete [] pbuf;
        delete [] bUsed;
       
        return (gMin);
}

gxqcn 发表于 2008-9-13 19:01:43

这么说来,我给出的程序效率是很高的,结果也是相当不错的。
谢谢你的代码,有空时我琢磨一下。

前面第26#说了,该问题更注重输出结果,因为是拿它作应用的;
即便运行时间长点也不碍事,反正只要运行一次。

当然,若有更优化的通用算法,则是上上品了。

无心人 发表于 2008-9-13 20:58:24

:)

为什么不用对数?
仅仅是因为是浮点运算?

假设某组数字的对数和为s
s <= 32
32 - s <= 1/16
是否算和优化的一组?

aimisiyou 发表于 2020-2-1 18:49:14

这跟一维下料是等价问题。只求满足根数最少的组合方式之一?(可能有多种)
页: 1 2 3 4 [5]
查看完整版本: 最少分组方案算法