求标准数独的初盘的尽量短的字符串表示

cuixiping 2009-07-27 11:14:01
标准数独,就是9x9的九宫数独,一些格子空着,一些格子填有数字,采用怎么样的数据结构将初盘表示为一个比较短的字符串呢?
比如这样一个初盘,点代表空格子。
....76.2.
.7.98..15
.....59..
....6..8.
9.......6
..2.1..37
.8..9...2
.37..1...
.5.......
但是如果用81个字符来表示就太冗长了,怎么能比较短的表示呢?
...全文
177 点赞 收藏 16
写回复
16 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
cuixiping 2009-08-06
按 xxjjs 的方法,并且简化了一下,没压那么厉害,不过也很好了,达到22.5字节了。

先去掉换行符,把(.)换成0,成为一个81位数字组成的字符串;
尾部的0全部删去;
3个比特一组表示连续0的数量,000 001 010 011分别表示1个、2个、3个、4个的连续0;
10000 - 10111 表示1到8的数字, 11表示数字9

这样做为二进制输出是很短了。
如果要做为文本输出,就转为64进制的字符串。

mathe说的算术编码我看了,很好,暂时没有去编写它。有空再弄弄。
算法真是好东东啊
回复
cuixiping 2009-08-05
多谢mathe! 我正学习你给的链接。
回复
fireseed 2009-08-03
这个值得好好研究一下!
回复
cuixiping 2009-08-03
mathe的博客里面有一篇《数独游戏程序》这里面的数独程序,将我上面的这个布局(随手选的一个开局)保存下来是21字节如下:

F0 41 C3 AB 04 A3 F9 80 7E 56 7C 56 42 E0 03 13 9E 82 64 7B 00


布局:
....76.2.
.7.98..15
.....59..
....6..8.
9.......6
..2.1..37
.8..9...2
.37..1...
.5.......
回复
mathe 2009-08-03
回复
绿色夹克衫 2009-08-03
LZ可以直接问问mathe,肯定可以解答你的问题。最近他来CSDN似乎不多,
不过你可以去http://bbs.emath.ac.cn/ 问问
回复
81字符不是很长的,嗯。
搂主难道要进行大规模九宫数据的存储?
如果不是的话,建议不进行压缩。


[Quote=引用楼主 cuixiping 的帖子:]
标准数独,就是9x9的九宫数独,一些格子空着,一些格子填有数字,采用怎么样的数据结构将初盘表示为一个比较短的字符串呢?
比如这样一个初盘,点代表空格子。
....76.2.
.7.98..15
.....59..
....6..8.
9.......6
..2.1..37
.8..9...2
.37..1...
.5.......
但是如果用81个字符来表示就太冗长了,怎么能比较短的表示呢?
[/Quote]
回复
cuixiping 2009-08-02
mathe的博客里面有一篇《数独游戏程序》,他的这个程序保存出来的数独开局往往只有20字节左右,少的18个字节,多的20出头或更多。
有谁知道怎么实现表示到这么短的么?
http://blog.csdn.net/mathe/archive/2007/08/23/1755672.aspx
回复
fireseed 2009-08-02
18个字节不可能,除非那个开局里的数字都很小,而且很少。

我用程序可以证明这一点,该程序可以将数独局进行haffman编码和解码,并可以将二进制码输出为字符串以方便复制。编码表已经按楼主所给的开局优化,最小编码是23个字节:07FFC3E43E7F70EF20EF1310EF1FCF8FEC80FC77CF0EF。如果需要通用编码,必须按顺序处理编码表。

代码没什么意义,就不写注释了,见谅。



#include <string>
#include <iostream>
#include <bitset>
#include <algorithm>
#include <map>

using namespace std;
typedef unsigned int uint;
map< char, uint > g_mapEncode;
map< uint, char > g_mapDecode;
const uint g_cnMaxBits = 512;

void Encode( const string &strData, string &strOut )
{
uint nNum = 0, nCurBit, nEndBit;
bitset<g_cnMaxBits> bsResult;
string::const_iterator iCurNum;
for ( nCurBit = 0, iCurNum = strData.begin();
iCurNum < strData.end(); ++iCurNum, ++nCurBit )
for ( uint j = 0; j < g_mapEncode[ *iCurNum ]; ++j )
bsResult.flip( nCurBit++ );
nEndBit = nCurBit;
for ( strOut.clear(), nCurBit = 0; nCurBit < nEndBit; nNum = 0 )
{
for ( uint j = 0; j < 4 && nCurBit < nEndBit; ++j )
nNum |= ( uint( bsResult[nCurBit++] ) << j );
strOut.push_back( nNum + ( ( nNum < 10 ) ? '0' : ( 'A' - 10 ) ) );
}
for ( iCurNum = strOut.end() - 1;
iCurNum >= strOut.begin() && *iCurNum == '0'; --iCurNum );
strOut.erase( iCurNum + 1, strOut.end() );
}


void Decode( const string &strData, string &strOut )
{
const uint cnMaxBits = 512;
uint nEndBit, nNum, nCurBit = 0, nLastNum = 0;
bitset<g_cnMaxBits> bsResult;
string::const_iterator iCurNum;
for ( iCurNum = strData.begin(); iCurNum < strData.end(); ++iCurNum )
{
nNum = *iCurNum - ( ( *iCurNum >= 'A' ) ? 'A' - 10 : '0' );
for ( int j = 0; j < 4; ++j, nNum >>= 1 )
bsResult.set( nCurBit++, ( nNum & 1 ) == 1 );
}
nEndBit = nCurBit;
for ( strOut.clear(), nCurBit = 0; nCurBit <= nEndBit; ++nCurBit )
if ( !bsResult.test( nCurBit ) )
{
strOut.push_back( g_mapDecode[ nCurBit - nLastNum ] );
nLastNum = nCurBit + 1;
}
strOut.resize( 81, '.' );
}

void main( int argc, char* argv[] )
{
int nIndex = 0;
g_mapEncode.insert( pair< char, uint >( '.', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '9', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '8', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '7', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '2', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '3', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '1', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '5', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '6', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '4', nIndex++ ) );
g_mapEncode.insert( pair< char, uint >( '0', nIndex++ ) );
for ( map< char, uint >::iterator cur = g_mapEncode.begin();
cur != g_mapEncode.end(); ++cur )
{
g_mapDecode.insert( pair<int, char>( cur->second, cur->first ) );
}
string strAll;
strAll = "....76.2."\
".7.98..15"\
".....59.."\
"....6..8."\
"9.......6"\
"..2.1..37"\
".8..9...2"\
".37..1..."\
".5.......";
string strOut;
Encode( strAll, strOut );
cout << "Encode(" << strOut.length() << "): " << strOut << endl;
Decode( strOut, strAll );
cout << "Decode: " << strAll << endl;
system( "pause" );
}

回复
xxjjs 2009-07-30
建议用bit加霍夫曼压缩来保存

数字1-9需要4bits

1. 99%的情况下置位空格少于80%,所以可以用1bit “0” 来表示空位,用1xxxx(5 bits)来表示置位空格。例如有20个空格置位,则可以仅用20×5 + 41 = 141bits,约18 bytes

2. 再有数字1-9转换为0-8,可以发现仅有数字8是“11”开头的,所以数字8可以仅用两bits,又可以节约(1/9) * (3/5)的置位空间, 上例中平均可以节约7bits,那么17bytes就够了

3. 再继续,显然有不少连续的0,可以用0xxx来表示xxx个连续的零,先统计下连续零的个数和种类来确定0的设置,例如“000”“000”和“00000”需要11位,如果设00为三个个零,010为一个零,011为两个零则仅需“00”,“00”“00011”9位。具体的找本压缩算法的数来看看。如果不想研究的话,在步骤二后直接调用通用的无损压缩就可以了,可以估计,如果量大的话,基本上平均一个数独有10个字节就够了
回复
LeonTown 2009-07-27
[Quote=引用 2 楼 sbwwkmyd 的回复:]
如果覆盖率低于50%,可以考虑用数组如{4,7},{5,6},{7,2},{10,7},{12,9}...
[/Quote]

或许还可以:
04760120270198...
其中,0标示空格,0后紧跟的数字为连续空格的数量。
在显示时,自己控制分行
回复
adfas 2009-07-27
最坏情况必须81,还有啥好犹豫的
回复
showjim 2009-07-27
[Quote=引用 2 楼 sbwwkmyd 的回复:]
如果覆盖率低于50%,可以考虑用数组如{4,7},{5,6},{7,2},{10,7},{12,9}...
[/Quote]
甚至可以精确分位,不过太麻烦了,不值得
回复
showjim 2009-07-27
如果覆盖率低于50%,可以考虑用数组如{4,7},{5,6},{7,2},{10,7},{12,9}...
回复
fire_woods 2009-07-27
81已经很短了.
没必要追求更短,如果你非要,稀疏矩阵可能会适合你.
回复
donkey301 2009-07-27
用字符太浪费,4bit表示一个0-9的数字就行了。这样就能少一半了。
回复
发动态
发帖子
数据结构与算法
创建于2007-08-27

3.2w+

社区成员

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