I am looking for an efficient algorithm for the following problem:
There is an array with values, i.e. (note that index 0 is omitted on purpose)
Index 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
Value 17, 12, 5, 22, 3, 12, 6, 13, 7, 0, 2, 15
What I need to find is a subset of indices under these constraints:
The number of indices is constant (i.e. 3)
The sum of indices is constant (i.e. 20)
Each index may only appear once (so [2, 9, 9] is not a valid solution)
The sum of values is maximum.
For example if the subset length is 3 and the sum is 20, all possible solutions would be
Indices: [1, 7, 12] Sum of values: 17 + 6 + 15 = 38
Indices: [1, 8, 11] Sum of values: 17 + 13 + 2 = 32
Indices: [1, 9, 10] Sum of values: 17 + 7 + 0 = 24
Indices: [2, 6, 12] Sum of values: 12 + 12 + 15 = 39
Indices: [2, 7, 11] Sum of values: 12 + 6 + 2 = 20
Indices: [2, 8, 10] Sum of values: 12 + 13 + 0 = 25
Indices: [3, 5, 12] Sum of values: 5 + 3 + 15 = 23
Indices: [3, 6, 11] Sum of values: 5 + 12 + 2 = 19
Indices: [3, 7, 10] Sum of values: 5 + 6 + 0 = 11
Indices: [3, 8, 9] Sum of values: 5 + 13 + 7 = 25
Indices: [4, 5, 11] Sum of values: 22 + 3 + 2 = 27
Indices: [4, 6, 10] Sum of values: 22 + 12 + 0 = 34
Indices: [4, 7, 9] Sum of values: 22 + 6 + 7 = 35
Indices: [5, 6, 9] Sum of values: 3 + 12 + 7 = 22
Indices: [5, 7, 8] Sum of values: 3 + 6 + 13 = 22
of which [2, 6, 12] is the optimal solution because it has the maximum sum of values.
At the moment I run through all possible combinations using a slightly modified partition algorithm which grows exponentially as the sum of indices grows, so I wonder if there is any better way?
Solution O(I.S.K)
Let's do some naming first:
I is the greatest index (12 in your example)
S is the sum of values whose indices are selected (20 in your example)
K is the number of selected indices
V[] the array of values linked to the indices
maxsum(s, i, k) the maximal sum reachable by using k indices, all differents, whose value is less than or equal to i and whose sum is s.
Then you want to find maxsum(S, I, K)
Your problem exhibits some good properties:
optimal sub-structure
redundant sub-problems
For instance, when trying to compute maxsum(s, i, k) I can either not use index i, in which case the value is maxsum(s, i-1, k). Or I could use index i. In this case, I want to solve the sub-problem: what is the maximum sum reachable by indices less than or equal to i-1 and whose sum is s-i using k-1 such indices. This is the value: V[i] + maxsum(s-i, i-1, k-1).
As we want to reach the maximal sum we end up having: (Edit: corrected maxsum(s-i, i-1, k) to maxsum(s-i, i-1, k-1))
maxsum(s, i, k) = max{ maxsum(s, i-1, k) ; V[i] + maxsum(s-i, i-1, k-1) }
This is typical of a problem solvable by dynamic programming.
Here is an example C++ program solving the problem in O(I.S.K) (space and time).
We can improve the space complexity to O(I.S) at the price of a bigger time complexity: O(I.S.K²).
How to use the program
g++ -std=c++14 -g -Wall -O0 dp.cpp -o dp
./dp input.txt
Where input.txt is a file with the following format:
first line contains three integers: I S K
second line contains I integers, the values of the indices
Example run
---- K=1 ----
17 12 5 22 3 12 6 13 7 0 2 15
[ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10][11][12]
[ 1] 17 17 17 17 17 17 17 17 17 17 17 17
[ 2] 12 12 12 12 12 12 12 12 12 12 12
[ 3] 5 5 5 5 5 5 5 5 5 5
[ 4] 22 22 22 22 22 22 22 22 22
[ 5] 3 3 3 3 3 3 3 3
[ 6] 12 12 12 12 12 12 12
[ 7] 6 6 6 6 6 6
[ 8] 13 13 13 13 13
[ 9] 7 7 7 7
[10] 0 0 0
[11] 2 2
[12] 15
[13]
[14]
[15]
[16]
[17]
[18]
[19]
[20]
---- K=2 ----
17 12 5 22 3 12 6 13 7 0 2 15
[ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10][11][12]
[ 1]
[ 2] 12 12 12 12 12 12 12 12 12 12 12
[ 3] 29 29 29 29 29 29 29 29 29 29 29
[ 4] 22 22 22 22 22 22 22 22 22 22
[ 5] 17 39 39 39 39 39 39 39 39 39
[ 6] 34 34 34 34 34 34 34 34 34
[ 7] 27 27 29 29 29 29 29 29 29
[ 8] 8 24 24 24 24 24 24 24
[ 9] 25 25 25 30 30 30 30 30
[10] 34 34 34 34 34 34 34
[11] 15 28 28 28 28 28 28
[12] 9 35 35 35 35 35
[13] 18 18 29 29 29 32
[14] 25 25 25 25 27
[15] 19 19 19 24 24
[16] 13 13 13 37
[17] 20 20 20 20
[18] 13 13 27
[19] 7 15 21
[20] 9 28
---- K=3 ----
17 12 5 22 3 12 6 13 7 0 2 15
[ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10][11][12]
[ 1]
[ 2]
[ 3]
[ 4]
[ 5] 17 17 17 17 17 17 17 17 17 17
[ 6] 34 34 34 34 34 34 34 34 34 34
[ 7] 51 51 51 51 51 51 51 51 51
[ 8] 44 44 44 44 44 44 44 44 44
[ 9] 39 39 41 41 41 41 41 41 41
[10] 42 42 42 42 42 42 42 42
[11] 37 51 51 51 51 51 51 51
[12] 30 46 46 46 46 46 46 46
[13] 39 40 52 52 52 52 52
[14] 20 35 47 47 47 47 47
[15] 37 37 42 42 42 42 44
[16] 31 37 37 37 41 41
[17] 40 40 40 40 40 54
[18] 21 47 47 47 47 49
[19] 41 41 41 41 44
[20] 22 35 35 35 39
index: 12 sum: 20
index: 6 sum: 8
index: 2 sum: 2
max sum: 39
The source code
#include <cstdio>
#include <iomanip>
#include <iostream>
#include <limits>
#include <valarray>
#include <vector>
using namespace std;
auto const INF = numeric_limits<double>::infinity();
struct matrix {
matrix(size_t rows, size_t cols, double value)
: cells(value, rows*cols)
, rows(rows)
, cols(cols)
, value(value)
{}
double& operator() (int r, int c)
{
if(r < 0 || c < 0)
return value;
return cells[r*cols+c];
}
valarray<double> cells;
size_t rows;
size_t cols;
double value;
};
int main(int argc, char* argv[]) {
if(argc > 1)
freopen(argv[1], "r", stdin);
// I: max index
// S: sum of indices
// K: number of indices in the sum S
int I, S, K;
cin >> I >> S >> K;
// load values
vector<double> V(I+1, 0);
for(int i=1; i<=I; ++i)
cin >> V[i];
// dynamic programming:
// --------------------
// maxsum(i, s, k) is the maximal sum reachable using 'k' indices, less
// than or equal to 'i', all differents, and having a sum of 's'
//
// maxsum(i, s, k) =
// -oo if i > s
//
// -oo if i < s && k == 1
//
// V[s] if i >= s && s <= I && k == 1
// -oo if (i < s || s > I) && k == 1
//
// max { V[i] + maxsum(i-1, S-i, k-1), maxsum(i-1, S, k) }
vector<matrix> maxsum(K+1, matrix(S+1, I+1, -INF));
// initialize K=1
for(int s=0; s<=I && s<=S; ++s) {
for(int i=s; i<=I; ++i) {
maxsum[1](s, i) = V[s];
}
}
// K > 1
for(int k=2; k<=K; ++k) {
for(int s=2; s<=S; ++s) {
for(int i=1; i<=I; ++i) {
auto l = V[i] + maxsum[k-1](s-i, i-1);
auto r = maxsum[k](s, i-1);
maxsum[k](s, i) = max(l, r);
}
}
}
// display the whole dynamic programming tables (optional)
for(int k=1; k<=K; ++k) {
cout << "---- K=" << k << " ----\n";
cout << " ";
for(int i=1; i<=I; ++i) {
cout << setw(3) << V[i] << ' ';
}
cout << '\n';
cout << " ";
for(int i=1; i<=I; ++i) {
cout << '[' << setw(2) << i << ']';
}
cout << '\n';
for(int s=1; s<=S; ++s) {
cout << '[' << setw(2) << s << "] ";
for(int i=1; i<=I; ++i) {
if(maxsum[k](s, i) == -INF) {
cout << " ";
} else {
cout << setw(3) << maxsum[k](s, i) << ' ';
}
}
cout << '\n';
}
}
// output the indices belonging to the solution by working backward in the
// dynamic programming tables
int t_S = S;
int t_I = I;
for(int k=K; k>=1; --k) {
if(t_I <= 0 || t_S <= 0) {
cout << "error...\n";
break;
}
auto m = maxsum[k](t_S, t_I);
int i;
for(i=t_I; i>=1; --i) {
if(maxsum[k](t_S, i) != m)
break;
}
cout << "index: " << setw(3) << (i+1) << ' ';
cout << "sum: " << setw(3) << t_S << '\n';
t_I = i;
t_S = t_S - i - 1;
}
cout << "max sum: " << maxsum[K](S, I) << '\n';
}
Take the arrays, and sort them by value instead of by index (keeping the index-value pairs preserved). Now, starting at the end of the array, take the last k numbers in the indices array, where k is the number of indices you have to have, and sum them up. If it equals the desired sum, great- you are done. If not, take note of the difference (desired sum - actual sum), and add that to the (n - k)th index. Find that index in the index array (ordered by value, mind you), now find your new sum of values (you can optimize this by subtracting out the old index's value and add the new one, instead of recomputing the sum of all k values).
You now have one valid solution, and a lower bound. You know the indices of the rest of the valid solution that can even possibly beat this score must come after the smallest index's value in the value-sorted array. That is:
Both sorted by value-
indices: | bunch of indices | index we found | more | k-1 'random' indices |
values: | bunch of values | value for ^ | more | k-1 largest values |
So we only have to search 'more' and the k-1 largest values for valid indices that satisfy the criteria and also have values that form a larger sum. To do this, we rinse and repeat, moving the smallest of the (n-k-1) elements backwards one, so we effectively try all combination of these elements, but in the order of decreasing subset-sums of our set of k elements. This allows us to continually narrow the space we search as we find larger sums, because know for certain that any sum that contains a smaller value than that of best solution will have a smaller sum (because the rest of the set is already as large as possible).
Pseudo Code:
pair_array = input() // each pair consists of index and value
sort_by_value(pair_array)
best_sum = 0
markers = [n - (k-1) .. n] // mark the k-1 indices being summed
while True:
sum_of_indices = sum_indices(pair_array[markers])
value_sum = sum_value(pair_array[markers])
if pair_array.contains(desired_sum - sum_of_indices): // this lets us effectively reduce our search by a factor of N, given contains uses a hashtable
value_sum += pair_array(pair_array.index(desired_sum - sum_of_indices)).value
if value_sum > best_sum:
best_sum = value_sum
pair_array.remove(0 .. n - (k-1)) // this greatly reduces the combinations checked
if has_next_combination(markers, pair_array):
next_greatest_combination(markers, pair_array) // pick new markers, in a reverse-binary counting fashion (most significant bit first way)
else:
print(best_sum)
break
One small trick I can think if you try to find lets say 3 indexes, is instead of iterating for the 3 indexes you can calculate the 3rd index when you know the first two indexes. For example when you know that
p1 = 1, p2 = 7 => p3 = 20 - (p1 + p2) = 12
This can be generalized when having N indexes the last one can always be inferred from the N-1 previous indexes.
I tried this in Python:
Index = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
Value = [17, 12, 5, 22, 3, 12, 6, 13, 7, 0, 2, 15 ]
maxSum = 0 SumInd = 20
for p1 in range(1,len(Index)+1):
for p2 in range(p1,len(Index)+1):
if (p1 + p2 < SumInd) and (p1 != p2):
p3 = SumInd - (p1 + p2) #If you know p1 and p2 calculate p3 index
if (p3 < len(Index)+1) and (p2 != p3) and (p1 != p3):
fooSum = Value[p1-1]+Value[p2-1] + Value[p3-1]
print(p1,p2,p3 , "Sum is ",fooSum)
if maxSum < fooSum:
maxSum = fooSum
print("Max Sum is ", maxSum)
Of course you need to save the indexes when you find maxSum. Also this implementation calculate many similar pairs (e.g. [1,9,10] and [9,10,1]). Perhaps a better solution could eliminate this pairs.
EDIT: Big Improvement I found a way to eliminate most unnecessary checks. Let's say you need 3 indexes. The first one checks the whole range of possible values. Let's say it is index1.
The other two indexes must sum at 20 - ind1 let's call this rest. The list of indexes is always ordered so you can have a back index showing the smallest value(first item in the list bigger than index1) and a front index that show the biggest values (last item in the list). So index2 = backIndex , index3 = frontIndex.
If the rest is smaller than the sum of index2,3 you could increase the back index (get the next bigger value) or if it is larger you decrease the front index until the two indexes meet each other where you break and increase index1. This eliminates checking (1,7,12) and (1,12,7) two times.
The code is here in Python:
maxSum = 0
SumInd = 20
for index_1 in range(1,len(Index)):
rest = SumInd - index_1
backIndex = index_1+1
frontIndex = len(Index)
while backIndex < frontIndex:
if rest > (backIndex + frontIndex):
backIndex = backIndex + 1
elif rest < (backIndex + frontIndex):
frontIndex = frontIndex - 1
else:
fooSum = Value[index_1-1]+Value[backIndex-1] + Value[frontIndex-1]
print("Checking for ",index_1,backIndex,frontIndex,' Sum of values:',fooSum)
if maxSum < fooSum:
indList = [index_1-1,backIndex,frontIndex]
maxSum = fooSum
backIndex = backIndex + 1 #To avoid Inf loop
print("Max Sum is ", maxSum,"at",indList)
and gives these results:
Checking for 1 7 12 Sum of values: 38
Checking for 1 8 11 Sum of values: 32
Checking for 1 9 10 Sum of values: 24
Checking for 2 6 12 Sum of values: 39
Checking for 2 7 11 Sum of values: 20
Checking for 2 8 10 Sum of values: 25
Checking for 3 5 12 Sum of values: 23
Checking for 3 6 11 Sum of values: 19
Checking for 3 7 10 Sum of values: 11
Checking for 3 8 9 Sum of values: 25
Checking for 4 5 11 Sum of values: 27
Checking for 4 6 10 Sum of values: 34
Checking for 4 7 9 Sum of values: 35
Checking for 5 6 9 Sum of values: 22
Checking for 5 7 8 Sum of values: 22
Max Sum is 39 at [1, 6, 12]
This can always be generalized for N indexes. The first N-2 indexes can search the whole range of the list (like index 1 in the case above, also it should be noted that all these indexes start checking from previous index value plus one until the end of the list to eliminate many duplicate checks).
The last two indexes can be calculated like I showed in my code and avoid many duplicate checks.
I want to create and simulate a wired topology using NS2. Trying to write a tcl and positioning the nodes and links using rand() .
My solution was:
### Create a simulator object
set ns [new Simulator]
set num_node 10
set num_flow 5
set x_dim 150
set y_dim 150
### Define different colors for data flows (for NAM)
$ns color 1 Blue
$ns color 2 Red
### Open the NAM trace file
set nf [open out.nam w]
$ns namtrace-all $nf
set tracefd [open out.tr w]
$ns trace-all $tracefd
### set up topography object
set topo [new Topography]; # This is needed for wireless
$topo load_flatgrid $x_dim $y_dim; # Setting a 2D space for the nodes
### Define a 'finish' procedure
proc finish {} {
global ns nf tracefd
$ns flush-trace
### Close the NAM trace file
close $nf
close $tracefd
### Execute NAM on the trace file
exec nam out.nam &
exit 0
}
#Create four nodes
for {set i 0} {$i < [expr $num_node]} {incr i} {
set n($i) [$ns node]
puts "Created node $i"
}
### Create links between the nodes
for {set i 0} {$i < [expr $num_node + 1]} {incr i} {
set s_node [expr int($num_node*rand())]; # src node
set d_node $s_node
while {$d_node==$s_node} { ; # while the random pair are same node
set d_node [expr int($num_node*rand())]; # dest node
}
$ns duplex-link $n($s_node) $n($d_node) 2Mb 10ms DropTail
$ns queue-limit $n($s_node) $n($d_node) 50
puts "Linking $s_node and $d_node"
}
### Give node position (for NAM)
set i 0
while {$i < $num_node } {
### Set random position for nodes
set x_pos [expr int($x_dim*rand())]; # random settings
set y_pos [expr int($y_dim*rand())]; # random settings
$n($i) set X_ $x_pos
$n($i) set Y_ $y_pos
$n($i) set Z_ 0.0
puts "Put $i to ($x_pos , $y_pos)"
#puts -nonewline $topofile "$i x: [$node_($i) set X_] y: [$node_($i) set Y_] \n"
incr i;
};
### Setup UDP connections
for {set i 0} {$i < [expr $num_flow]} {incr i} {
set s_node [expr int($num_node*rand())]; # src node
set d_node $s_node
while {$d_node==$s_node} {; # while the random pair are same node
set d_node [expr int($num_node*rand())]; # dest node
}
set udp($i) [new Agent/UDP]
$udp($i) set class_ $i
$ns attach-agent $n($s_node) $udp($i)
set null($i) [new Agent/Null]
$ns attach-agent $n($d_node) $null($i)
$ns connect $udp($i) $null($i)
$udp($i) set fid_ $i
puts "Flow $s_node - $d_node"
}
### Setup a CBR over UDP connections
for {set i 0} {$i < [expr $num_flow]} {incr i} {
set cbr($i) [new Application/Traffic/CBR]
$cbr($i) attach-agent $udp($i)
$cbr($i) set type_ CBR
$cbr($i) set packet_size_ 1000
$cbr($i) set rate_ 1mb
$cbr($i) set random_ false
puts "setting cbr for $i"
}
### Schedule events for the CBR and FTP agents
for {set i 0} {$i < [expr $num_flow]} {incr i} {
$ns at 0.1 "$cbr($i) start"
}
for {set i 0} {$i < [expr $num_flow]} {incr i} {
$ns at 4.5 "$cbr($i) stop"
}
for {set i 0} {$i < [expr $num_node] } { incr i} {
$ns initial_node_pos $n($i) 4
}
### Run the simulation
$ns run
But the randomization is often creating erroneous links and thus problem in simulation and getting this error :
--- Classfier::no-slot{} default handler (tcl/lib/ns-lib.tcl) ---
_o28: no target for slot 4294967295
_o28 type: Classifier/Hash/Dest
content dump:
classifier _o28
0 offset
0 shift
1073741823 mask
1 slots
slot 5: _o268 (Classifier/Port)
-1 default
---------- Finished standard no-slot{} default handler ----------
But this is also random and does not occur always. When it does not occur , the nam file shows that there have been duplicate definition of nodes.
Can someone please just give me some guidance about how to create a random wired topology with random valid links?
Your file "random-wired.tcl" is working OK here ... PCLinuxOS 2017.04 - x86_64.
$ ns235-64-orig random-wired.tcl
Created node 0
Created node 1
Created node 2
Created node 3
Created node 4
Created node 5
Created node 6
Created node 7
Created node 8
Created node 9
Linking 9 and 0
Linking 9 and 8
Linking 5 and 8
Linking 1 and 6
Linking 9 and 6
Linking 8 and 0
Linking 1 and 4
Linking 3 and 7
Linking 8 and 7
Linking 1 and 2
Linking 9 and 0
Put 0 to (139 , 71)
Put 1 to (107 , 146)
Put 2 to (14 , 9)
Put 3 to (16 , 23)
Put 4 to (89 , 30)
Put 5 to (26 , 65)
Put 6 to (46 , 76)
Put 7 to (87 , 31)
Put 8 to (12 , 105)
Put 9 to (89 , 56)
Flow 6 - 4
Flow 0 - 1
Flow 2 - 8
Flow 5 - 2
Flow 2 - 3
setting cbr for 0
setting cbr for 1
setting cbr for 2
setting cbr for 3
setting cbr for 4
The files out.nam 3.3MB, out.tr 1.4MB are created. And nam : Some nodes show activity.
--- Classfier::no-slot{} default handler (tcl/lib/ns-lib.tcl) ---
... is an known error with some simulations / some Linux OS´s. Another ns2 friendly OS is CentOS 7 - 64bit : Your file runs OK. The not so good news : Fails with Ubuntu 16.04 - 64 and Ubuntu 17.04 - 64. Actually Ubuntu isn't the first choice for ns2. Not very ns2 friendly with it's "different" patching of e.g. libc6.
Examples, rand() : aodv18.tcl, aodv_802_15_4.tcl, AODV-Jenova.tcl, aodv-Soumia.tcl, AODV-testcode-rand.tcl → https://drive.google.com/file/d/0B7S255p3kFXNMXRfTTlEcm5KUW8/view?usp=sharing
EDIT, May26 2017:
--- Classfier::no-slot{} default handler
I think a solution was found http://www.linuxquestions.org/questions/linux-software-2/ns2-2-35-antnet-4175532576/#14 (post #14 #newthink) → Add $ns multicast :
set ns [ new Simulator ]
$ns multicast
Works perfect with the problematic Antnet simulations.
$ns multicast didn't work me at times. I found this post which says that this error occurs when there the generated graph is disconnected.
So i kept a check that the graph never turns out to be disconnected and every node has at least one link.
#nodeFlag_ keeps of which nodes has at least one link
for {set i 0} {$i < $num_node} {incr i} {
set nodeFlag_($i) 0
}
### Create links between the nodes
for {set i 0} {$i < [expr $num_node + 1]} {incr i} {
set s_node [expr int($num_node*rand())]; # src node
set d_node $s_node
while {$d_node==$s_node} { ; # while the random pair are same node
set d_node [expr int($num_node*rand())]; # dest node
}
$ns duplex-link $node_($s_node) $node_($d_node) 2Mb 10ms DropTail
set nodeFlag_($s_node) 1
set nodeFlag_($d_node) 1
$ns queue-limit $node_($s_node) $node_($d_node) 50
puts "Linking $s_node and $d_node"
}
for {set i 0} {$i < $num_node} {incr i} {
#see here
if {$nodeFlag_($i) == 0} {
set random [expr int($num_node*rand())]
$ns duplex-link $node_($i) $node_($random) 2Mb 10ms DropTail
puts "left link $i and $random"
}
puts "$nodeFlag_($i)"
}
Hope this helps.
EDIT : I was wrong. The error still shows up in some simulations.
Although your last edit makes sure that every node has at least one link, it does not assure to have a fully connected graph among all the nodes. In other words, you might still end up having multiple isolated groups of nodes that can not be reached from other nodes.
I have a grid where the columns we can represent as ABC and the rows can be represented as x, y, z types for example.
There could be multiple rows that are all classified as the same type. This is not true of the columns.
Rows of the same type should not be combined or "mixed." And all combining needs to be in the order ABC, not CBA or anyhting else. Here is an example I've come up with.
I need to print out every combination (in columnal order) of the following table:
A B C
--------------
x | 10 20 30
x | 11 21 31
y | 40 50 60
y | 41 51 61
z | 70 80 90
The output needs to be like this (it doesn't have to output the pattern, that's just for reference):
(Pattern) (Result)
Ax Bx Cx {10 20 30} {11 21 31} (notice no mix-combinations on same letter x)
Ax Bx Cy {10 20 60} {10 20 61} {11 21 60} {11 21 61}
Ax Bx Cz {10 20 90} {11 21 90}
Ax By Cx {10 50 30} {10 51 30} {11 50 31} {11 51 31}
Ax By Cy {10 50 60} {10 51 61} {11 50 60} {11 51 61}
Ax By Cz {10 50 90} {10 51 90} {11 50 90} {11 51 90}
Ax Bz Cx {10 80 30} {11 80 31}
Ax Bz Cy {10 80 60} {10 80 61} {11 80 60} {11 80 61}
Ax Bz Cz {10 80 90} {11 80 90}
Ay Bx Cx {40 20 30} {40 21 31} {41 20 30} {41 21 31}
Ay Bx Cy ...
Ay Bx Cz ...
Ay By Cx ...
Ay By Cy ...
Ay By Cz ...
Ay Bz Cx ...
Ay Bz Cy ...
Ay Bz Cz ...
Az Bx Cx ...
Az Bx Cy ...
Az Bx Cz ...
Az By Cx ...
Az By Cy ...
Az By Cz ...
Az Bz Cx ...
Az Bz Cy ...
Az Bz Cz {30 60 90}
I have some Tcl code I've started making to do this but its not great. It doesn't take multiple rows of the same x y or z into consideration but here's what I got so far:
set dl {0 1 2}
set op {x y z}
set debug [open "debugloop.txt" "w"]
set i 0
set j 0
set k 0
set e 0
set r 0
set s 0
set g yes
while {$g} {
puts $debug A[lindex $op $i][lindex $dl $e]B[lindex $op $j][lindex $dl $r]C[lindex $op $k][lindex $dl $s]
incr s
if {$s > 2} {
puts $debug ""
incr r
set s 0
if {$r > 2} {
puts $debug ""
incr e
set r 0
if {$e > 2} {
puts $debug ""
incr k
set e 0
if {$k > 2} {
puts $debug ""
incr j
set k 0
if {$j > 2} {
puts $debug ""
incr i
set j 0
if {$i > 2} {
set g no
}
}
}
}
}
}
}
does anyone have a better way to do this than a series of hardcoded nested loops? I've had a lot of trouble with this
There are 2 main parts to your problem:
Generating all of the (Pattern) combinations
Storing the data in a way that allows you to look up the (Result) for each combination.
For the first of these you need to generate all of the permutations with repetition allowed of your Pattern values, x,y,z, in your example. There is some code for this on the tcl wiki.
In your case order is important, {x,y,z} is not the same as {z,y,x} so the algorithm needs to take that into account. Here is some code that uses a simple algorithm to generate the repeating permutations, it uses the idea that you can generate all of the permutations by counting up modulo the number of elements. The number of permutations grows pretty quickly, look at the way permCount is calculated!
# Permutaions
proc NextPerm {perm values} {
set result {}
set needIncr 1
foreach val $perm {
if { $needIncr == 1} {
set newVal [lindex $values [expr {[lsearch -exact $values $val] + 1}]]
if {$newVal != ""} {
# New value was found
lappend result $newVal
set needIncr 0
} else {
# No next value found so we need to carry
lappend result [lindex $values 0]
}
} else {
lappend result $val
}
}
return $result
}
set values {x y z}
set perm {x x x}
puts $perm
set permCount [expr {[llength $perm] ** [llength $perm]}]
for {set i 1} {$i < $permCount} {incr i} {
set perm [NextPerm $perm $values]
puts $perm
}
NB: I've made no attempt to optimise this code.
If the pattern values never change then rather than generate them yourself you could use an online resource like this (there are lots of other sites if you do a search) to generate the values and hard-code them into your program.
For 2 I'd look at storing the values in an array or a dict with a key that allows you to pull back the relevant values.
hi I need to generate 30 random numbers without any repeatations of numbers in TCL.
Here is the code to generate random number which works fine, but will generate redundant numbers.
proc myRand { min max } {
set maxFactor [expr [expr $max + 1] - $min]
set value [expr int([expr rand() * 100])]
set value [expr [expr $value % $maxFactor] + $min]
return $value
}
for {set i 1} {$i < 31} {incr i} {
upvar 0 fnode($i) fnod($i)
set fnod($i) [myRand 1 20] ;# random number is generated between 1 to 20
}
Anyone please help out.
To generate a list of random numbers without repetitions, you've got to put in code to explicitly prevent them. In general, random sequences most certainly can contain repetitions, just as if you toss a coin, it will sometimes come up heads twice (or more) in a row.
set r -1; # Some value that definitely isn't in the sequence
for {set i 1} {$i < 31} {incr i} {
upvar 0 fnode($i) fnod($i)
while {$r == [set r [myRand 1 20]]} {
# Empty body
}
set fnod($i) $r; # Random number is generated between 1 to 20
}
Note that if you're picking 30 values from a collection of 20 numbers, you'll necessarily (by the pigeonhole principle) get some repetitions. But we can prevent values from occurring twice in a row.
Your random number generator is slightly horrifying too. This is the idiomatic version:
proc myRand {min max} {
set range [expr {$max - $min + 1}]
return [expr {$min + int(rand() * $range)}]
}
Code to generate a sequence of unique random numbers could be written like this, but it won't work unless $nnums is less than or equal to $rmax.
set nnums 30
set rmax 20
set nums {}
if {$nnums > $rmax} {
puts "You can't get $nnums unique values from a range of 1 to $rmax!"
} else {
while {[llength $nums] < $nnums} {
set n [myRand 1 $rmax]
if {$n ni $nums} {lappend nums $n}
}
set nums [linsert $nums 0 {}]
for {set i 1} {$i <= $nnums} {incr i} {
set fnod($i) [lindex $nums $i]
}
}
(When I started writing this answer, I was to preoccupied to notice that you were trying to get 30 unique numbers from a 1-20 range, which is impossible, as others have pointed out.)
There are some other problems with your code. You don't need to do nested calls to expr:
expr [expr $max + 1] - $min
# is the same as
expr {$max + 1 - $min}
so your random number generator can be written like this:
proc myRand {min max} {
expr {int(rand() * 100) % ($max + 1 - $min) + $min}
}
but that is still more calculations than necessary. This version is better:
proc myRand {min max} {
expr {int(rand() * ($max + 1 - $min)) + $min}
}
You can also use this:
package require math
::math::random 1 21
(Note 21, not 20!)