Difference between revisions of "User:Strife Onizuka/String Tree"
m |
m |
||
(3 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
This is just a bit of silliness, it's sanity has not been vetted. The purpose of this code is to look at the costs of splitting a string into chunks of a specific size. The problem with getting all sequential chunks of a specific set size is that doing so is '''O(N<sup>2</sup>)'''. This script hopes to reduce that. As previously stated, I have no idea if it does. It probably doesn't. I'm hoping for something like '''O(N*log<sub>2</sub>(N))'''. | This is just a bit of silliness, it's sanity has not been vetted. The purpose of this code is to look at the costs of splitting a string into chunks of a specific size. The problem with getting all sequential chunks of a specific set size is that doing so is '''O(N<sup>2</sup>)'''. This script hopes to reduce that. As previously stated, I have no idea if it does. It probably doesn't. I'm hoping for something like '''O(N*log<sub>2</sub>(N))'''. | ||
Results: It works! At 25 chunks you break even between just doing flat llGetSubString vs this. Changing how split is done will improve that. | Results: It works! At 25 chunks you break even between just doing flat llGetSubString vs this. Changing how split is done will improve that. Whether or not it's faster I don't know, I've been running this in LSLEditor. My goal has been to reduce read bottlenecks on the string. The cost equation appears to be '''N * (2.5 * log<sub>2</sub>(N) + 1)''' | ||
Of course this comes at a huge memory cost. You should eliminate all "DEBUG:Self" lines. If you want you can chuck <code>clear</code> and <code>setup</code>. Remove "DEBUG:Usage" after your code works. | |||
<lsl> | <lsl> | ||
integer chunk = | integer chunk = 0; | ||
list buffer; | list buffer; | ||
string last; | string last; | ||
integer cost; //DEBUG | integer cost; //DEBUG:Self | ||
string getNext() { | string getNext() { | ||
last = llList2String(buffer, -1); | last = llList2String(buffer, -1); //This can be made local and the global "last" eliminated. Don't forget to mod setup and clear. | ||
buffer = llDeleteSubList(buffer, -1, -1); | buffer = llDeleteSubList(buffer, -1, -1); | ||
integer size = llStringLength(last); | integer size = llStringLength(last); | ||
cost += ((size + chunk - 1) / chunk); //DEBUG | cost += ((size + chunk - 1) / chunk); //DEBUG:Self | ||
if(size > chunk) { | if(size > chunk) { | ||
//The advantage of keeping the tree unbalanced this way is that split only needs to be cleverly calculated once. | //The advantage of keeping the tree unbalanced this way is that split only needs to be cleverly calculated once. | ||
integer split = (1 << (llCeil(llLog((size + chunk - 1) / chunk) * 1.4426950408889634073599246810019) - 1)) * chunk; | integer split = (1 << (llCeil(llLog((size + chunk - 1) / chunk) * 1.4426950408889634073599246810019) - 1)) * chunk; | ||
do { | do { | ||
cost += ((size + chunk - 1) / chunk) * 2; //DEBUG | cost += ((size + chunk - 1) / chunk) * 2; //DEBUG:Self | ||
buffer += llGetSubString(last, split, -1); | buffer += llGetSubString(last, split, -1); | ||
last = llDeleteSubString(last, split, -1); | last = llDeleteSubString(last, split, -1); | ||
size = split | split = (size = split) >> 1; | ||
} while(size > chunk); | } while(size > chunk); | ||
} else if (!size) | } | ||
else if (!size) chunk = 0;//This line can be eliminated if you don't use chunk to test for readiness | |||
return last; | return last; | ||
} | } | ||
setup(string str, integer size) { | setup(string str, integer size) { //This function can be eliminated, just inline the setup of buffer and chunk. | ||
if( | if(!chunk) llOwnerSay("Warning: Buffer may still be in use!"); //DEBUG:Usage | ||
buffer = [str]; | buffer = [str]; | ||
chunk = size; | chunk = size; | ||
last = ""; | last = ""; | ||
cost = 0; //DEBUG | cost = 0; //DEBUG:Self | ||
size = (llStringLength(str) + chunk - 1) / chunk; //DEBUG | size = (llStringLength(str) + chunk - 1) / chunk; //DEBUG:Self | ||
llOwnerSay("Flat: " + ((size * (size + 1)) / 2) + " Chunks: " + chunk); //DEBUG | llOwnerSay("Flat: " + ((size * (size + 1)) / 2) + " Chunks: " + chunk); //DEBUG:Self | ||
} | } | ||
clear() { | clear() { //This function can be eliminated or inlined. If you don't depend upon chunk or last then you can eliminate it. | ||
chunk = | chunk = 0; | ||
last = ""; | last = ""; | ||
} | } |
Latest revision as of 16:34, 14 March 2014
This is just a bit of silliness, it's sanity has not been vetted. The purpose of this code is to look at the costs of splitting a string into chunks of a specific size. The problem with getting all sequential chunks of a specific set size is that doing so is O(N2). This script hopes to reduce that. As previously stated, I have no idea if it does. It probably doesn't. I'm hoping for something like O(N*log2(N)).
Results: It works! At 25 chunks you break even between just doing flat llGetSubString vs this. Changing how split is done will improve that. Whether or not it's faster I don't know, I've been running this in LSLEditor. My goal has been to reduce read bottlenecks on the string. The cost equation appears to be N * (2.5 * log2(N) + 1)
Of course this comes at a huge memory cost. You should eliminate all "DEBUG:Self" lines. If you want you can chuck clear
and setup
. Remove "DEBUG:Usage" after your code works.
<lsl>
integer chunk = 0;
list buffer;
string last;
integer cost; //DEBUG:Self
string getNext() {
last = llList2String(buffer, -1); //This can be made local and the global "last" eliminated. Don't forget to mod setup and clear. buffer = llDeleteSubList(buffer, -1, -1); integer size = llStringLength(last); cost += ((size + chunk - 1) / chunk); //DEBUG:Self if(size > chunk) { //The advantage of keeping the tree unbalanced this way is that split only needs to be cleverly calculated once. integer split = (1 << (llCeil(llLog((size + chunk - 1) / chunk) * 1.4426950408889634073599246810019) - 1)) * chunk; do { cost += ((size + chunk - 1) / chunk) * 2; //DEBUG:Self buffer += llGetSubString(last, split, -1); last = llDeleteSubString(last, split, -1); split = (size = split) >> 1; } while(size > chunk); } else if (!size) chunk = 0;//This line can be eliminated if you don't use chunk to test for readiness return last;
}
setup(string str, integer size) { //This function can be eliminated, just inline the setup of buffer and chunk.
if(!chunk) llOwnerSay("Warning: Buffer may still be in use!"); //DEBUG:Usage buffer = [str]; chunk = size; last = ""; cost = 0; //DEBUG:Self size = (llStringLength(str) + chunk - 1) / chunk; //DEBUG:Self llOwnerSay("Flat: " + ((size * (size + 1)) / 2) + " Chunks: " + chunk); //DEBUG:Self
}
clear() { //This function can be eliminated or inlined. If you don't depend upon chunk or last then you can eliminate it.
chunk = 0; last = "";
}
default {
state_entry() { setup("12341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234", 1); string value; while(value = getNext()) ;//llOwnerSay(value); llOwnerSay("Cost: " + cost); }
} </lsl>