Although the algo still doesn't match what is really going on (if anyone else can make it more correct then please modify it and post the updated results and source) I thought I just do one "slightly more realistic" 10 year analysis:
blocks = 5256000
a = 1
b = 1
c = 1
d = 1
e = 1
f = 1
g = 1
h = 1
i = 1
j = 1
k = 5
l = 5
m = 5
n = 5
o = 10
p = 10
q = 10
r = 10
s = 30
wins( a ) = 1159
wins( b ) = 1174
wins( c ) = 1150
wins( d ) = 1183
wins( e ) = 1193
wins( f ) = 1169
wins( g ) = 1143
wins( h ) = 1177
wins( i ) = 1115
wins( j ) = 1205
wins( k ) = 175337
wins( l ) = 175600
wins( m ) = 174960
wins( n ) = 174629
wins( o ) = 580246
wins( p ) = 579058
wins( q ) = 580492
wins( r ) = 578307
wins( s ) = 2225703
best_streak( a ) = 2
best_streak( b ) = 1
best_streak( c ) = 1
best_streak( d ) = 1
best_streak( e ) = 1
best_streak( f ) = 1
best_streak( g ) = 1
best_streak( h ) = 2
best_streak( i ) = 2
best_streak( j ) = 2
best_streak( k ) = 5
best_streak( l ) = 4
best_streak( m ) = 5
best_streak( n ) = 4
best_streak( o ) = 7
best_streak( p ) = 7
best_streak( q ) = 6
best_streak( r ) = 6
best_streak( s ) = 19
best_combined_streak = 2
(combined the first 6 accounts)
With this simulation we see a combined streak of only 2 when we have grouped together a bunch of very small stakeholders.
I then also tried simulating the idea that forgers will be penalised with higher stake holders being penalised more often but every way I try that I simply end up with worse results (in terms of streaks) like this:
blocks = 5256000
a = 1
b = 1
c = 1
d = 1
e = 1
f = 1
g = 1
h = 1
i = 1
j = 1
k = 5
l = 5
m = 5
n = 5
o = 10
p = 10
q = 10
r = 10
s = 30
wins( a ) = 22634
wins( b ) = 21929
wins( c ) = 21654
wins( d ) = 21987
wins( e ) = 21739
wins( f ) = 21888
wins( g ) = 21492
wins( h ) = 21921
wins( i ) = 21824
wins( j ) = 21706
wins( k ) = 324659
wins( l ) = 318268
wins( m ) = 317664
wins( n ) = 318776
wins( o ) = 635268
wins( p ) = 640399
wins( q ) = 631070
wins( r ) = 630736
wins( s ) = 1220386
penalised( a ) = 62
penalised( b ) = 63
penalised( c ) = 70
penalised( d ) = 53
penalised( e ) = 66
penalised( f ) = 62
penalised( g ) = 61
penalised( h ) = 66
penalised( i ) = 54
penalised( j ) = 57
penalised( k ) = 934
penalised( l ) = 983
penalised( m ) = 976
penalised( n ) = 963
penalised( o ) = 1471
penalised( p ) = 1454
penalised( q ) = 1485
penalised( r ) = 1479
penalised( s ) = 2012
best_streak( a ) = 3
best_streak( b ) = 3
best_streak( c ) = 3
best_streak( d ) = 3
best_streak( e ) = 3
best_streak( f ) = 3
best_streak( g ) = 4
best_streak( h ) = 3
best_streak( i ) = 4
best_streak( j ) = 3
best_streak( k ) = 9
best_streak( l ) = 8
best_streak( m ) = 7
best_streak( n ) = 7
best_streak( o ) = 22
best_streak( p ) = 20
best_streak( q ) = 18
best_streak( r ) = 13
best_streak( s ) = 39
best_combined_streak = 14
(combined the first 6 accounts)
So although it is admittedly not an accurate simulation in my opinion getting large accounts split up into smaller accounts and having lots of pools would probably do a better job of "securing" the blockchain than introducing penalties at all.
// Copyright (c) 2014 CIYAM Developers
//
// Distributed under the MIT/X11 software license, please refer to the file license.txt
// in the root project directory or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
#include
#include
#include
//#define NUM_DAYS 1
#define NUM_YEARS 10
#define PENALISE_HIGHER_STAKE
//#define PREVENT_IMMEDIATE_REPEAT
//#define SHOW_WINNERS
//#define SHOW_WINNERS_WEIGHT
using namespace std;
const int c_combined_proportion = 3;
#ifndef NUM_YEARS
const size_t c_num_blocks = 1440 * NUM_DAYS;
#else
const size_t c_num_blocks = 1440 * 365 * NUM_YEARS;
#endif
int main( )
{
#ifdef SHOW_WINNERS
string winners;
#endif
vector< int > wins;
vector< int > streaks;
vector< int > balances;
vector< int > penalised;
vector< int > penalising;
vector< int > best_streak;
vector< int > combined;
vector< long > weights;
long total_balance = 0;
int combined_streak = 0;
int best_combined_streak = 0;
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 1 );
balances.push_back( 5 );
balances.push_back( 5 );
balances.push_back( 5 );
balances.push_back( 5 );
balances.push_back( 10 );
balances.push_back( 10 );
balances.push_back( 10 );
balances.push_back( 10 );
balances.push_back( 30 );
srand( ( unsigned int )time( 0 ) );
for( size_t i = 0; i < balances.size( ); i++ )
{
total_balance += balances[ i ];
wins.push_back( 0 );
weights.push_back( 0 );
streaks.push_back( 0 );
combined.push_back( 0 );
penalised.push_back( 0 );
penalising.push_back( 0 );
best_streak.push_back( 0 );
}
size_t last_winner = 0;
for( size_t blocks = 0; blocks < c_num_blocks; blocks++ )
{
long total_weight = 0;
for( size_t i = 0; i < weights.size( ); i++ )
{
if( penalising[ i ] )
{
--penalising[ i ];
weights[ i ] = 0;
}
else
{
int divisor = rand( ) % 10;
if( divisor == 0 )
++divisor;
weights[ i ] = ( rand( ) % 10000 ) * ( balances[ i ] / divisor );
if( weights[ i ] == 0 )
++weights[ i ];
total_weight += weights[ i ];
}
}
if( total_weight == 0 )
++total_weight;
size_t winner = 0;
size_t runner_up = 0;
long best_target = 0;
long second_best_target = 0;
for( size_t i = 0; i < balances.size( ); i++ )
{
long adjusted_weight = weights[ i ] * 1000 / total_weight;
if( adjusted_weight > best_target )
{
winner = i;
best_target = adjusted_weight;
}
else if( adjusted_weight > second_best_target )
{
runner_up = i;
second_best_target = adjusted_weight;
}
}
#ifdef PENALISE_HIGHER_STAKE
if( best_target == 350 ) // an above median value
{
++penalised[ winner ];
penalising[ winner ] = 1440;
}
#endif
#ifdef PREVENT_IMMEDIATE_REPEAT
if( winner == last_winner )
winner = runner_up;
#endif
#ifdef SHOW_WINNERS
winners += ( char )( 'a' + winner );
# ifdef SHOW_WINNERS_WEIGHT
ostringstream osstr;
osstr << best_target;
winners += "(" + osstr.str( ) + ")";
# endif
#endif
++wins[ winner ];
if( winner < balances.size( ) / c_combined_proportion )
{
++combined_streak;
if( combined_streak > best_combined_streak )
best_combined_streak = combined_streak;
}
else
combined_streak = 0;
if( winner == last_winner )
{
++streaks[ winner ];
if( streaks[ winner ] > best_streak[ winner ] )
best_streak[ winner ] = streaks[ winner ];
}
else
streaks[ winner ] = 0;
last_winner = winner;
}
cout << "blocks = " << c_num_blocks << endl;
for( size_t i = 0; i < balances.size( ); i++ )
cout << ( char )( 'a' + i ) << " = " << balances[ i ] << endl;
#ifdef SHOW_WINNERS
cout << winners << endl;
#endif
for( size_t i = 0; i < wins.size( ); i++ )
cout << "wins( " << ( char )( 'a' + i ) << " ) = " << wins[ i ] << endl;
#ifdef PENALISE_HIGHER_STAKE
for( size_t i = 0; i < penalised.size( ); i++ )
cout << "penalised( " << ( char )( 'a' + i ) << " ) = " << penalised[ i ] << endl;
#endif
for( size_t i = 0; i < best_streak.size( ); i++ )
cout << "best_streak( " << ( char )( 'a' + i ) << " ) = " << ( best_streak[ i ] + 1 ) << endl;
cout << "best_combined_streak = " << best_combined_streak << endl;
cout << "(combined the first " << ( balances.size( ) / c_combined_proportion ) << " accounts)" << endl;
}