How to generate Set Y with multiple elements in CPLEX? - set

I have written this code to generate a Set Y, with single element
int m=3 ;
range I= 1..m;
int w[i in I]=i;
int q= min(i in I)w[i] ;
int W=1000;
int Ea[I];
{int} B={381,198,291};
{int} E ={rand(f) | f in B: f>0};
execute
{
writeln("E is ", E)
var j=1
for(var k in E)
{
Ea[j]=k; //Array Ea has same values as set E
j=j+1;
}
}
int ok[i in I]=(sum(i in I)Ea[i]*w[i]<=W-q);
{int} Y= {sum(i in I)Ea[i]*w[i]|x in 0..W-q , i in I: ok[i]==1 } ;
execute{
writeln(Y);
}
The output of above code and variable values are
E is {93 42 31}
Y is {270}
Variable Values
How can I generate multiple elements in Set Y, since the rand function has been used while calculating E?

You can use arrays for several casts:
{int} B={381,198,291};
range casts=1..10;
{int} E[c in casts] ={rand(f) | f in B: f>0};
execute
{
writeln(E);
}
int Y[c in casts]= sum(e in E[c]) e;
execute{
writeln(Y);
}
gives
[{93 42 31} {378 131 243} {25 177 61} {4 48 212} {276 1 256} {289 138 264}
{366 192 177} {138 150 164} {125 163 246} {315 180 240}]
[166 752 263 264 533 691 735 452 534 735]

Related

Spiral in sampled x-y plane

Let’s say I have the following 3D discretized space, in which the indexes of the samples/nodes are sequential as it is shown in the picture.
Now consider only the horizontal middle layer.
My objective is to find a programmatically and iterative rule/s that allow me to run a spiral (like the image or similar, it can start in any direction) over the mid-layer, starting from node 254, as it is shown on the image:
As you can see in the picture, the yellow crosses show the nodes to be explored. In the first lap these nodes are consecutive while in the second they are separated by 1 node and so on.
I started to solve the problem as follows (pseudocode):
I considered size(y) = y = 13
Size(z) = z = 3
Lap 1:
254 – z * y = 215
254 – z * (y + 1) = 212
254 – z = 251
254 + z * (y - 1) = 290
254 + z * y = 293
254 + z * (y + 1) = 296
254 + z = 257
254 – z * (y – 1) = 218
Lap 2:
254 – 3 * z * y = 137
254 – 3 * z * (y + 2/3) = 131
…
But I think there may be a simpler, more general rule.
each direction has constant index increment:
const int dx = 39;
const int dy = 3;
const int dz = 1;
so to make a spiral you just start from start index and increment in current direction i-times then rotate by 90 deg and do the same ... then increment i and do this until desired size is hit ...
You should also add range checking so your spiral will not go outside your array as that would screw things up. By checking actual x,y,z coordinates. So either compute them in parallel or infer them from ix using modular arithmetics so for example something like (C++):
const int dx = 39;
const int dy = 3;
const int dz = 1;
int cw[4]={-dx,-dy,+dx,+dy}; // CW rotation
int ix=254; // start point (center of spiral)
int dir=0; // direction cw[dir]
int n=5; // size
int i,j,k,x,y,z,a; // temp
for (k=0,i=1;i<=n;i+=k,k^=1,dir++,dir&=3)
for (j=1;j<=i;j++)
{
int a=ix-1;
z = a% 3; a/= 3; // 3 is z-resolution
y = a%13; a/=13; // 13 is y-resolution
x = a;
if ((x>=0)&&(x<13)&&(y>=0)&&(y<13)&&(z>=0)&&(z<3))
{
// here use point ix
// Form1->mm_log->Lines->Add(AnsiString().sprintf("%i (%i,%i,%i) %i",ix,x,y,z,i));
}
ix+=cw[dir];
}
producing this output
ix x,y,z i
254 (6,6,1) 1
215 (5,6,1) 1
212 (5,5,1) 2
251 (6,5,1) 2
290 (7,5,1) 2
293 (7,6,1) 2
296 (7,7,1) 3
257 (6,7,1) 3
218 (5,7,1) 3
179 (4,7,1) 3
176 (4,6,1) 3
173 (4,5,1) 3
170 (4,4,1) 4
209 (5,4,1) 4
248 (6,4,1) 4
287 (7,4,1) 4
326 (8,4,1) 4
329 (8,5,1) 4
332 (8,6,1) 4
335 (8,7,1) 4
338 (8,8,1) 5
299 (7,8,1) 5
260 (6,8,1) 5
221 (5,8,1) 5
182 (4,8,1) 5
143 (3,8,1) 5
140 (3,7,1) 5
137 (3,6,1) 5
134 (3,5,1) 5
131 (3,4,1) 5
In case you want CCW spiral either reverse the cw[] or instead of dir++ do dir--
In case you want to have changeable screw width then you just increment i by the actual width instead of just by one.
Based on #Spektre answer, this code worked for me:
const int x_res = 13;
const int y_res = 13;
const int z_res = 3;
const int dx = 39;
const int dy = 3;
const int dz = 1;
int cw[4]={-dx,-dy,+dx,+dy}; // CW rotation
int ix=254; // start point (center of spiral)
int dir=0; // direction cw[dir]
int n=30; // size
int i,j,k;
cout << ix << endl;
// first "lap" (consecutive nodes)
for (k=0,i=1;i<=2;i+=k,k^=1,dir++,dir&=3)
for (j=1;j<=i;j++)
{
ix+=cw[dir];
cout << ix << endl;
}
i-=1;
int width = 2; //screw width
i+=width;
int dist = 1; //nodes separation
int node_count = 0; //nodes counter
for (k=k,i=i;i<=n;i+=k,k^=width,dir++,dir&=3)
{
if (dir==1)
{
dist+=1;
}
for (j=1;j<=i;j++)
{
ix+=cw[dir];
node_count +=1;
if ((0 < ix) && (ix <= x_res*y_res*z_res))
{
if (node_count == dist)
{
cout << ix << endl;
node_count = 0;
}
}
else return 0;
}
}
return 0;
with this output:
254 215 212 251 290 293 296 257 218 179 140 134 128 206 284 362 368 374 380 302
224 146 68 59 50 83 200 317 434 443 452 461 386 269 152 35

Algorithm for visiting all grid cells in pseudo-random order that has a guaranteed uniformity at any stage

Context:
I have a hydraulic erosion algorithm that needs to receive an array of droplet starting positions. I also already have a pattern replicating algorithm, so I only need a good pattern to replicate.
The Requirements:
I need an algorism that produces a set of n^2 entries in a set of format (x,y) or [index] that describe cells in an nxn grid (where n = 2^i where i is any positive integer).
(as a set it means that every cell is mentioned in exactly one entry)
The pattern [created by the algorism ] should contain zero to none clustering of "visited" cells at any stage.
The cell (0,0) is as close to (n-1,n-1) as to (1,1), this relates to the definition of clustering
Note
I was/am trying to find solutions through fractal-like patterns built through recursion, but at the time of writing this, my solution is a lookup table of a checkerboard pattern(list of black cells + list of white cells) (which is bad, but yields fewer artifacts than an ordered list)
C, C++, C#, Java implementations (if any) are preferred
You can use a linear congruential generator to create an even distribution across your n×n space. For example, if you have a 64×64 grid, using a stride of 47 will create the pattern on the left below. (Run on jsbin) The cells are visited from light to dark.
That pattern does not cluster, but it is rather uniform. It uses a simple row-wide transformation where
k = (k + 47) mod (n * n)
x = k mod n
y = k div n
You can add a bit of randomness by making k the index of a space-filling curve such as the Hilbert curve. This will yield the pattern on the right. (Run on jsbin)
     
     
You can see the code in the jsbin links.
I have solved the problem myself and just sharing my solution:
here are my outputs for the i between 0 and 3:
power: 0
ordering:
0
matrix visit order:
0
power: 1
ordering:
0 3 2 1
matrix visit order:
0 3
2 1
power: 2
ordering:
0 10 8 2 5 15 13 7 4 14 12 6 1 11 9 3
matrix visit order:
0 12 3 15
8 4 11 7
2 14 1 13
10 6 9 5
power: 3
ordering:
0 36 32 4 18 54 50 22 16 52 48 20 2 38 34 6
9 45 41 13 27 63 59 31 25 61 57 29 11 47 43 15
8 44 40 12 26 62 58 30 24 60 56 28 10 46 42 14
1 37 33 5 19 55 51 23 17 53 49 21 3 39 35 7
matrix visit order:
0 48 12 60 3 51 15 63
32 16 44 28 35 19 47 31
8 56 4 52 11 59 7 55
40 24 36 20 43 27 39 23
2 50 14 62 1 49 13 61
34 18 46 30 33 17 45 29
10 58 6 54 9 57 5 53
42 26 38 22 41 25 37 21
the code:
public static int[] GetPattern(int power, int maxReturnSize = int.MaxValue)
{
int sideLength = 1 << power;
int cellsNumber = sideLength * sideLength;
int[] ret = new int[cellsNumber];
for ( int i = 0 ; i < cellsNumber && i < maxReturnSize ; i++ ) {
// this loop's body can be used for per-request computation
int x = 0;
int y = 0;
for ( int p = power - 1 ; p >= 0 ; p-- ) {
int temp = (i >> (p * 2)) % 4; //2 bits of the index starting from the begining
int a = temp % 2; // the first bit
int b = temp >> 1; // the second bit
x += a << power - 1 - p;
y += (a ^ b) << power - 1 - p;// ^ is XOR
// 00=>(0,0), 01 =>(1,1) 10 =>(0,1) 11 =>(1,0) scaled to 2^p where 0<=p
}
//to index
int index = y * sideLength + x;
ret[i] = index;
}
return ret;
}
I do admit that somewhere along the way the values got transposed, but it does not matter because of how it works.
After doing some optimization I came up with this loop body:
int x = 0;
int y = 0;
for ( int p = 0 ; p < power ; p++ ) {
int temp = ( i >> ( p * 2 ) ) & 3;
int a = temp & 1;
int b = temp >> 1;
x = ( x << 1 ) | a;
y = ( y << 1 ) | ( a ^ b );
}
int index = y * sideLength + x;
(the code assumes that c# optimizer, IL2CPP, and CPP compiler will optimize variables temp, a, b out)

How do I make this program work for input >10 for the USACO Training Pages Square Palindromes?

Problem Statement -
Given a number base B (2 <= B <= 20 base 10), print all the integers N (1 <= N <= 300 base 10) such that the square of N is palindromic when expressed in base B; also print the value of that palindromic square. Use the letters 'A', 'B', and so on to represent the digits 10, 11, and so on.
Print both the number and its square in base B.
INPUT FORMAT
A single line with B, the base (specified in base 10).
SAMPLE INPUT
10
OUTPUT FORMAT
Lines with two integers represented in base B. The first integer is the number whose square is palindromic; the second integer is the square itself. NOTE WELL THAT BOTH INTEGERS ARE IN BASE B!
SAMPLE OUTPUT
1 1
2 4
3 9
11 121
22 484
26 676
101 10201
111 12321
121 14641
202 40804
212 44944
264 69696
My code works for all inputs <=10, however, gives me some weird output for inputs >10.
My Code-
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int baseToBase(int num, int base) //accepts a number in base 10 and the base to be converted into as arguments
{
int result=0, temp=0, i=1;
while(num>0)
{
result = result + (num%base)*pow(10, i);
i++;
num = num/base;
}
result/=10;
return result;
}
long long int isPalin(int n, int base) //checks the palindrome
{
long long int result=0, temp, num=n*n, x=n*n;
num = baseToBase(num, base);
x = baseToBase(x, base);
while(num)
{
temp=num%10;
result = result*10 + temp;
num/=10;
}
if(x==result)
return x;
else
return 0;
}
int main()
{
int base, i, temp;
long long int sq;
cin >> base;
for(i=1; i<=300; i++)
{
temp=baseToBase(i, base);
sq=isPalin(i, base);
if(sq!=0)
cout << temp << " " << sq << endl;
}
return 0;
}
For input = 11, the answer should be
1 1
2 4
3 9
6 33
11 121
22 484
24 565
66 3993
77 5335
101 10201
111 12321
121 14641
202 40804
212 44944
234 53535
While my answer is
1 1
2 4
3 9
6 33
11 121
22 484
24 565
66 3993
77 5335
110 10901
101 10201
111 12321
121 14641
209 40304
202 40804
212 44944
227 50205
234 53535
There is a difference in my output and the required one as 202 shows under 209 and 110 shows up before 101.
Help appreciated, thanks!
a simple example for B = 11 to show error in your base conversion is for i = 10 temp should be A but your code calculates temp = 10. Cause in we have only 10 symbols 0-9 to perfectly show every number in base 10 or lower but for bases greater than that you have to use other symbols to represent a different digit like 'A', 'B' and so on. problem description clearly states that. Hope You will be able to fix your code now by modifying your int baseToBase(int num, int base)function.

what is the best way to generate random pattern inside of a table

I'v got a table (2d array), c x r. Need to generate a random pattern of connected cells inside of it. No self-crossings and no diagonal-moves. See related picture for example. ex. 1
с = 6, r = 7, the pattern is shown in numbers.
I'w wrote a function for this and it works fine, but I'm looking for hard optimization. In the code below you can see that if the pattern gets into a dead end it just rebuilds itself from the start. That is very inefficient if the pattern length is close or equals to the number of cells, c*r (42 in the example). So some smart solution is needed for this, like moving the whole pattern symmetrically when it runs out of possible moves or to add some analytics to the function so it never cathes up in the dead ends. Again, for the low values of c, r and patternLength my example works fine, but I'm looking for algorithmic perfection and high performance even on pretty high numbers.
function ClassLogic:generatePattern()
--[[ subfunctions ]]
--choosing next point for the pattern
local move = function( seq )
--getting the last sequence point
local last = seq[#seq]
-- checking the nearness of walls
local
wallLeft,
wallRight,
wallUp,
wallDown =
(last.c==1),
(last.c==config.tableSize.c),
(last.r==1),
(last.r==config.tableSize.r)
-- checking the nearness of already sequenced points
local
spLeft,
spRight,
spUp,
spDown =
(utilities.indexOfTable( seq, { c = last.c - 1, r = last.r } )~=-1),
(utilities.indexOfTable( seq, { c = last.c + 1, r = last.r } )~=-1),
(utilities.indexOfTable( seq, { c = last.c, r = last.r - 1 } )~=-1),
(utilities.indexOfTable( seq, { c = last.c, r = last.r + 1 } )~=-1)
local leftRestricted = (wallLeft or spLeft)
local rightRestricted = (wallRight or spRight)
local upRestricted = (wallUp or spUp)
local downRestricted = (wallDown or spDown)
if ( leftRestricted and rightRestricted and upRestricted and downRestricted ) then
-- dead end
print('d/e')
return nil
else
-- go somewhere possible
local possibleDirections = {}
if (not leftRestricted) then possibleDirections[#possibleDirections+1] = 1 end
if (not rightRestricted) then possibleDirections[#possibleDirections+1] = 2 end
if (not upRestricted) then possibleDirections[#possibleDirections+1] = 3 end
if (not downRestricted) then possibleDirections[#possibleDirections+1] = 4 end
local direction = possibleDirections[math.random( 1, #possibleDirections )]
if (direction==1) then
--next point is left
return { c = last.c - 1, r = last.r }
elseif (direction==2) then
--next point is right
return { c = last.c + 1, r = last.r }
elseif (direction==3) then
--next point is up
return { c = last.c, r = last.r - 1 }
elseif (direction==4) then
--next point is down
return { c = last.c, r = last.r + 1 }
end
end
end
--[[ subfunctions end ]]
-- choose random entry point
local entry = { c = math.random( 1, config.tableSize.c ),
r = math.random( 1, config.tableSize.r ) }
-- start points sequence
local pointSequence = { [1] = entry }
-- building the pattern
local succeed = false
while (not succeed) do
for i = 2, self.patternLength do
local nextPoint = move( pointSequence )
if (nextPoint~=nil) then
pointSequence[i] = nextPoint
if (i==self.patternLength) then succeed = true end
else
pointSequence = { [1] = entry }
break
end
end
end
return pointSequence
end
Any ideas or approaches on how this could be realized would be highly appreciated. Maybe some recursive backtracker or a pathfinding or a random-walk algorithms?
The snake-style growing is not enough for good performance.
The main idea is to randomly modify the path being generated by adding small detours like the following:
- - 6 - - - - 8 - -
- - 5 - - - 6 7 - -
- - 4 1 - ===> - 5 4 1 -
- - 3 2 - - - 3 2 -
- - - - - - - - - -
(note the additional two cells added to the left of 4-5 segment)
Such implementation works very fast for area filling < 95%
local function generate_path(W, H, L)
-- W = field width (number of columns) -- c = 1..W
-- H = field height (number of rows) -- r = 1..H
-- L = path length, must be within range 1..W*H
assert(L >= 1 and L <= W * H, "Path length is greater than field area")
local function get_idx(x, y)
return x >= 1 and x <= W and y >= 1 and y <= H and (y - 1) * W + x
end
local function get_x_y(idx)
local x = (idx - 1) % W + 1
local y = (idx - x) / W + 1
return x, y
end
local function random_sort(array)
for last = #array, 2, -1 do
local pos = math.random(last)
array[pos], array[last] = array[last], array[pos]
end
end
local path_sum_x = 0
local path_sum_y = 0
local path_ctr = 0
local is_unused = {} -- [idx] = true/nil (or idx recently swapped with)
local function mark_as_unused(idx, value)
local x, y = get_x_y(idx)
path_sum_x = path_sum_x - x
path_sum_y = path_sum_y - y
path_ctr = path_ctr - 1
is_unused[idx] = value or true
end
local function mark_as_path(idx)
local x, y = get_x_y(idx)
path_sum_x = path_sum_x + x
path_sum_y = path_sum_y + y
path_ctr = path_ctr + 1
is_unused[idx] = nil
end
for x = 1, W do
for y = 1, H do
is_unused[get_idx(x, y)] = true
end
end
-- create path of length 1 by selecting random cell
local idx = get_idx(math.random(W), math.random(H))
mark_as_path(idx)
local path = {first = idx, last = idx, [idx] = {}}
-- path[idx] == {next=next_idx/nil, prev=prev_idx/nil}
local function grow()
local variants = {
{dx=-1, dy=0, origin="last"}, {dx=1, dy=0, origin="last"},
{dx=0, dy=-1, origin="last"}, {dx=0, dy=1, origin="last"},
{dx=-1, dy=0, origin="first"}, {dx=1, dy=0, origin="first"},
{dx=0, dy=-1, origin="first"}, {dx=0, dy=1, origin="first"}
}
random_sort(variants)
for _, vector in ipairs(variants) do
local x, y = get_x_y(path[vector.origin])
local idx = get_idx(vector.dx + x, vector.dy + y)
if is_unused[idx] then
if vector.origin == 'first' then
-- add new first cell of the path
local old_first = path.first
path[old_first].prev = idx
path[idx] = {next = old_first}
path.first = idx
else
-- add new last cell of the path
local old_last = path.last
path[old_last].next = idx
path[idx] = {prev = old_last}
path.last = idx
end
mark_as_path(idx)
return true
end
end
end
local function shrink()
if math.random(2) == 2 then
-- remove first cell of the path
local old_first = path.first
local new_first = assert(path[old_first].next)
path[old_first] = nil
path.first = new_first
path[new_first].prev = nil
mark_as_unused(old_first)
else
-- remove last cell of the path
local old_last = path.last
local new_last = assert(path[old_last].prev)
path[old_last] = nil
path.last = new_last
path[new_last].next = nil
mark_as_unused(old_last)
end
end
local function inflate()
local variants = {}
local idx1 = path.first
repeat
local idx4 = path[idx1].next
if idx4 then
local x1, y1 = get_x_y(idx1)
local x4, y4 = get_x_y(idx4)
local dx14, dy14 = x4 - x1, y4 - y1
local dx, dy = dy14, dx14
for side = 1, 2 do
dx, dy = -dx, -dy
local x2, y2 = x1 + dx, y1 + dy
local idx2 = get_idx(x2, y2)
local idx3 = get_idx(x2 + dx14, y2 + dy14)
if is_unused[idx2] and is_unused[idx3] then
table.insert(variants, {idx1, idx2, idx3, idx4})
end
end
end
idx1 = idx4
until not idx4
if #variants > 0 then
local idx1, idx2, idx3, idx4 =
(table.unpack or unpack)(variants[math.random(#variants)])
-- insert idx2 and idx3 between idx1 and idx4
path[idx1].next = idx2
path[idx2] = {prev = idx1, next = idx3}
path[idx3] = {prev = idx2, next = idx4}
path[idx4].prev = idx3
mark_as_path(idx2)
mark_as_path(idx3)
return true
end
end
local function euclid(dx, dy)
return dx*dx + dy*dy
end
local function swap()
local variants = {}
local path_center_x = path_sum_x / path_ctr
local path_center_y = path_sum_y / path_ctr
local idx1 = path.first
repeat
local idx2 = path[idx1].next
local idx3 = idx2 and path[idx2].next
if idx3 then
local x1, y1 = get_x_y(idx1)
local x2, y2 = get_x_y(idx2)
local x3, y3 = get_x_y(idx3)
local dx12, dy12 = x2 - x1, y2 - y1
local dx23, dy23 = x3 - x2, y3 - y2
if dx12 * dx23 + dy12 * dy23 == 0 then
local x, y = x1 + dx23, y1 + dy23
local idx = get_idx(x, y)
local dist2 = euclid(x2 - path_center_x, y2 - path_center_y)
local dist = euclid(x - path_center_x, y - path_center_y)
if is_unused[idx] and dist2<dist and is_unused[idx]~=idx2 then
table.insert(variants, {idx1, idx2, idx3, idx})
end
end
end
idx1 = idx2
until not idx3
if #variants > 0 then
local idx1, idx2, idx3, idx =
(table.unpack or unpack)(variants[math.random(#variants)])
-- swap idx2 and idx
path[idx1].next = idx
path[idx] = path[idx2]
path[idx3].prev = idx
path[idx2] = nil
mark_as_unused(idx2, idx)
mark_as_path(idx)
return true
end
end
local actions = {grow, inflate, swap}
repeat
random_sort(actions)
local success
for _, action in ipairs(actions) do
success = action()
if success then
break
end
end
if not success and path_ctr < L then
-- erase and rewind
while path_ctr > 1 do
shrink()
end
end
until path_ctr >= L
while path_ctr > L do
shrink()
end
local pointSequence = {}
local idx = path.first
local step = 0
repeat
step = step + 1
path[idx].step = step
local x, y = get_x_y(idx)
pointSequence[step] = {c = x, r = y}
idx = path[idx].next
until not idx
local field = 'W = '..W..', H = '..H..', L = '..L..'\n'
for y = 1, H do
for x = 1, W do
local c = path[get_idx(x, y)]
field = field..(' '..(c and c.step or '-')):sub(-4)
end
field = field..'\n'
end
print(field)
return pointSequence
end
Usage example:
math.randomseed(os.time())
local pointSequence = generate_path(6, 7, 10)
-- pointSequence = {[1]={r=r1,c=c1}, [2]={r=r2,c=c2},...,[10]={r=r10,c=c10}}
Result examples:
W = 5, H = 5, L = 10
- - - 9 10
- 6 7 8 -
- 5 4 1 -
- - 3 2 -
- - - - -
W = 5, H = 5, L = 19
15 16 17 18 19
14 1 2 3 4
13 12 11 6 5
- - 10 7 -
- - 9 8 -
W = 6, H = 7, L = 35
- 35 34 25 24 23
- - 33 26 21 22
- 31 32 27 20 19
- 30 29 28 - 18
- 1 10 11 12 17
3 2 9 8 13 16
4 5 6 7 14 15
W = 19, H = 21, L = 394
77 78 79 84 85 118 119 120 121 122 123 124 125 126 127 128 129 254 255
76 75 80 83 86 117 116 115 114 141 140 139 138 135 134 131 130 253 256
73 74 81 82 87 88 89 112 113 142 145 146 137 136 133 132 - 252 257
72 69 68 67 92 91 90 111 - 143 144 147 148 149 150 151 152 251 258
71 70 65 66 93 108 109 110 163 162 161 160 159 158 157 156 153 250 259
58 59 64 63 94 107 166 165 164 191 192 193 196 197 - 155 154 249 260
57 60 61 62 95 106 167 168 189 190 - 194 195 198 241 242 243 248 261
56 55 54 53 96 105 170 169 188 203 202 201 200 199 240 239 244 247 262
47 48 51 52 97 104 171 172 187 204 205 206 231 232 237 238 245 246 263
46 49 50 99 98 103 174 173 186 209 208 207 230 233 236 267 266 265 264
45 42 41 100 101 102 175 184 185 210 211 228 229 234 235 268 269 270 271
44 43 40 39 38 177 176 183 214 213 212 227 226 225 276 275 274 273 272
33 34 35 36 37 178 179 182 215 216 217 218 223 224 277 278 279 280 281
32 29 28 23 22 - 180 181 12 11 10 219 222 287 286 285 284 283 282
31 30 27 24 21 18 17 14 13 8 9 220 221 288 289 290 291 292 293
380 381 26 25 20 19 16 15 394 7 4 3 304 303 300 299 296 295 294
379 382 383 384 387 388 391 392 393 6 5 2 305 302 301 298 297 312 313
378 371 370 385 386 389 390 347 346 343 342 1 306 307 308 309 310 311 314
377 372 369 364 363 350 349 348 345 344 341 340 333 332 319 318 317 316 315
376 373 368 365 362 351 352 353 354 355 338 339 334 331 320 321 322 323 324
375 374 367 366 361 360 359 358 357 356 337 336 335 330 329 328 327 326 325

Adjacent number algorithm grouper

By which I mean this:
Given the input set of numbers:
1,2,3,4,5 becomes "1-5".
1,2,3,5,7,9,10,11,12,14 becomes "1-3, 5, 7, 9-12, 14"
This is the best I managed to come up with: [C#]
Which feels a little sloppy to me, so the question is, is there somehow more readable and/or elegant solution to this?
public static string[] FormatInts(int[] ints)
{
if (ints == null)
throw new ArgumentNullException("ints"); // hey what are you doing?
if (ints.Length == 0)
return new string[] { "" }; // nothing to process
if (ints.Length == 1)
return new string[] { ints[0].ToString() }; // nothing to process
Array.Sort<int>(ints); // need to sort these lil' babies
List<string> values = new List<string>();
int lastNumber = ints[0]; // start with the first number
int firstNumber = ints[0]; // same as above
for (int i = 1; i < ints.Length; i++)
{
int current = ints[i];
int difference = (lastNumber - current ); // compute difference between last number and current number
if (difference == -1) // the numbers are adjacent
{
if (firstNumber == 0) // this is the first of the adjacent numbers
{
firstNumber = lastNumber;
}
else // we're somehow in the middle or at the end of the adjacent number set
{
lastNumber = current;
continue;
}
}
else
{
if (firstNumber > 0 && firstNumber != lastNumber) // get ready to print a set of numbers
{
values.Add(string.Format("{0}-{1}", firstNumber, lastNumber));
firstNumber = 0; // reset
}
else // print a single value
{
values.Add(string.Format("{0}", lastNumber));
}
}
lastNumber = current;
}
if (firstNumber > 0) // if theres anything left, print it out
{
values.Add(string.Format("{0}-{1}", firstNumber, lastNumber));
}
return values.ToArray();
}
I've rewritten your code like this:
public static string[] FormatInts(int[] ints)
{
Array.Sort<int>(ints);
List<string> values = new List<string>();
for (int i = 0; i < ints.Length; i++)
{
int groupStart = ints[i];
int groupEnd = groupStart;
while (i < ints.Length - 1 && ints[i] - ints[i + 1] == -1)
{
groupEnd = ints[i + 1];
i++;
}
values.Add(string.Format(groupEnd == groupStart ? "{0}":"{0} - {1}", groupStart, groupEnd));
}
return values.ToArray();
}
And then:
/////////////////
int[] myInts = { 1,2,3,5,7,9,10,11,12,14 };
string[] result = FormatInts(myInts); // now result haves "1-3", "5", "7", "9-12", "14"
See How would you display an array of integers as a set of ranges? (algorithm)
My answer to the above question:
void ranges(int n; int a[n], int n)
{
qsort(a, n, sizeof(*a), intcmp);
for (int i = 0; i < n; ++i) {
const int start = i;
while(i < n-1 and a[i] >= a[i+1]-1)
++i;
printf("%d", a[start]);
if (a[start] != a[i])
printf("-%d", a[i]);
if (i < n-1)
printf(",");
}
printf("\n");
}
Pure functional Python:
#!/bin/env python
def group(nums):
def collect((acc, i_s, i_e), n):
if n == i_e + 1: return acc, i_s, n
return acc + ["%d"%i_s + ("-%d"%i_e)*(i_s!=i_e)], n, n
s = sorted(nums)+[None]
acc, _, __ = reduce(collect, s[1:], ([], s[0], s[0]))
return ", ".join(acc)
assert group([1,2,3,5,7,9,10,11,12,14]) == "1-3, 5, 7, 9-12, 14"
I'm a bit late to the party, but anyway, here is my version using Linq:
public static string[] FormatInts(IEnumerable<int> ints)
{
var intGroups = ints
.OrderBy(i => i)
.Aggregate(new List<List<int>>(), (acc, i) =>
{
if (acc.Count > 0 && acc.Last().Last() == i - 1) acc.Last().Add(i);
else acc.Add(new List<int> { i });
return acc;
});
return intGroups
.Select(g => g.First().ToString() + (g.Count == 1 ? "" : "-" + g.Last().ToString()))
.ToArray();
}
Looks clear and straightforward to me. You can simplify a bit if you either assume the input array is sorted, or sort it yourself before further processing.
The only tweak I'd suggest would be to reverse the subtraction:
int difference = (current - lastNumber);
... simply because I find it easier to work with positive differences. But your code is a pleasure to read!
As I wrote in comment, I am not fan of the use of value 0 as flag, making firstNumber both a value and a flag.
I did a quick implementation of the algorithm in Java, boldly skipping the validity tests you already correctly covered...
public class IntListToRanges
{
// Assumes all numbers are above 0
public static String[] MakeRanges(int[] numbers)
{
ArrayList<String> ranges = new ArrayList<String>();
Arrays.sort(numbers);
int rangeStart = 0;
boolean bInRange = false;
for (int i = 1; i <= numbers.length; i++)
{
if (i < numbers.length && numbers[i] - numbers[i - 1] == 1)
{
if (!bInRange)
{
rangeStart = numbers[i - 1];
bInRange = true;
}
}
else
{
if (bInRange)
{
ranges.add(rangeStart + "-" + numbers[i - 1]);
bInRange = false;
}
else
{
ranges.add(String.valueOf(numbers[i - 1]));
}
}
}
return ranges.toArray(new String[ranges.size()]);
}
public static void ShowRanges(String[] ranges)
{
for (String range : ranges)
{
System.out.print(range + ","); // Inelegant but quickly coded...
}
System.out.println();
}
/**
* #param args
*/
public static void main(String[] args)
{
int[] an1 = { 1,2,3,5,7,9,10,11,12,14,15,16,22,23,27 };
int[] an2 = { 1,2 };
int[] an3 = { 1,3,5,7,8,9,11,12,13,14,15 };
ShowRanges(MakeRanges(an1));
ShowRanges(MakeRanges(an2));
ShowRanges(MakeRanges(an3));
int L = 100;
int[] anr = new int[L];
for (int i = 0, c = 1; i < L; i++)
{
int incr = Math.random() > 0.2 ? 1 : (int) Math.random() * 3 + 2;
c += incr;
anr[i] = c;
}
ShowRanges(MakeRanges(anr));
}
}
I won't say it is more elegant/efficient than your algorithm, of course... Just something different.
Note that 1,5,6,9 can be written either 1,5-6,9 or 1,5,6,9, not sure what is better (if any).
I remember having done something similar (in C) to group message numbers to Imap ranges, as it is more efficient. A useful algorithm.
Perl
With input validation/pre-sorting
You can easily get the result as a LoL if you need to do something more fancy than
just return a string.
#!/usr/bin/perl -w
use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
sub adjacenify {
my #input = #_;
# Validate and sort
looks_like_number $_ or
die "Saw '$_' which doesn't look like a number" for #input;
#input = sort { $a <=> $b } #input;
my (#output, #range);
#range = (shift #input);
for (#input) {
if ($_ - $range[-1] <= 1) {
push #range, $_ unless $range[-1] == $_; # Prevent repetition
}
else {
push #output, [ #range ];
#range = ($_);
}
}
push #output, [ #range ] if #range;
# Return the result as a string. If a sequence is size 1, then it's just that number.
# Otherwise, it's the first and last number joined by '-'
return join ', ', map { 1 == #$_ ? #$_ : join ' - ', $_->[0], $_->[-1] } #output;
}
print adjacenify( qw/1 2 3 5 7 9 10 11 12 14/ ), "\n";
print adjacenify( 1 .. 5 ), "\n";
print adjacenify( qw/-10 -9 -8 -1 0 1 2 3 5 7 9 10 11 12 14/ ), "\n";
print adjacenify( qw/1 2 4 5 6 7 100 101/), "\n";
print adjacenify( qw/1 62/), "\n";
print adjacenify( qw/1/), "\n";
print adjacenify( qw/1 2/), "\n";
print adjacenify( qw/1 62 63/), "\n";
print adjacenify( qw/-2 0 0 2/), "\n";
print adjacenify( qw/-2 0 0 1/), "\n";
print adjacenify( qw/-2 0 0 1 2/), "\n";
Output:
1 - 3, 5, 7, 9 - 12, 14
1 - 5
-10 - -8, -1 - 3, 5, 7, 9 - 12, 14
1 - 2, 4 - 7, 100 - 101
1, 62
1
1 - 2
1, 62 - 63
-2, 0, 2
-2, 0 - 1
-2, 0 - 2
-2, 0 - 2
And a nice recursive solution:
sub _recursive_adjacenify($$);
sub _recursive_adjacenify($$) {
my ($input, $range) = #_;
return $range if ! #$input;
my $number = shift #$input;
if ($number - $range->[-1] <= 1) {
return _recursive_adjacenify $input, [ #$range, $number ];
}
else {
return $range, _recursive_adjacenify $input, [ $number ];
}
}
sub recursive_adjacenify {
my #input = #_;
# Validate and sort
looks_like_number $_ or
die "Saw '$_' which doesn't look like a number" for #input;
#input = sort { $a <=> $b } #input;
my #output = _recursive_adjacenify \#input, [ shift #input ];
# Return the result as a string. If a sequence is size 1,
# then it's just that number.
# Otherwise, it's the first and last number joined by '-'
return join ', ', map { 2 == #$_ && $_->[0] == $_->[1] ? $_->[0] :
1 == #$_ ? #$_ :
join ' - ', $_->[0], $_->[-1] } #output;
}
Short and sweet Ruby
def range_to_s(range)
return range.first.to_s if range.size == 1
return range.first.to_s + "-" + range.last.to_s
end
def format_ints(ints)
range = []
0.upto(ints.size-1) do |i|
range << ints[i]
unless (range.first..range.last).to_a == range
return range_to_s(range[0,range.length-1]) + "," + format_ints(ints[i,ints.length-1])
end
end
range_to_s(range)
end
My first thought, in Python:
def seq_to_ranges(seq):
first, last = None, None
for x in sorted(seq):
if last != None and last + 1 != x:
yield (first, last)
first = x
if first == None: first = x
last = x
if last != None: yield (first, last)
def seq_to_ranges_str(seq):
return ", ".join("%d-%d" % (first, last) if first != last else str(first) for (first, last) in seq_to_ranges(seq))
Possibly could be cleaner, but it's still waaay easy.
Plain translation to Haskell:
import Data.List
seq_to_ranges :: (Enum a, Ord a) => [a] -> [(a, a)]
seq_to_ranges = merge . foldl accum (id, Nothing) . sort where
accum (k, Nothing) x = (k, Just (x, x))
accum (k, Just (a, b)) x | succ b == x = (k, Just (a, x))
| otherwise = (k . ((a, b):), Just (x, x))
merge (k, m) = k $ maybe [] (:[]) m
seq_to_ranges_str :: (Enum a, Ord a, Show a) => [a] -> String
seq_to_ranges_str = drop 2 . concatMap r2s . seq_to_ranges where
r2s (a, b) | a /= b = ", " ++ show a ++ "-" ++ show b
| otherwise = ", " ++ show a
About the same.
Transcript of an interactive J session (user input is indented 3 spaces, text in ASCII boxes is J output):
g =: 3 : '<#~."1((y~:1+({.,}:)y)#y),.(y~:(}.y,{:y)-1)#y'#/:~"1
g 1 2 3 4 5
+---+
|1 5|
+---+
g 1 2 3 5 7 9 10 11 12 14
+---+-+-+----+--+
|1 3|5|7|9 12|14|
+---+-+-+----+--+
g 12 2 14 9 1 3 10 5 11 7
+---+-+-+----+--+
|1 3|5|7|9 12|14|
+---+-+-+----+--+
g2 =: 4 : '<(>x),'' '',>y'/#:>#:(4 :'<(>x),''-'',>y'/&.>)#((<#":)"0&.>#g)
g2 12 2 14 9 1 3 10 5 11 7
+---------------+
|1-3 5 7 9-12 14|
+---------------+
(;g2) 5 1 20 $ (i.100) /: ? 100 $ 100
+-----------------------------------------------------------+
|20 39 82 33 72 93 15 30 85 24 97 60 87 44 77 29 58 69 78 43|
| |
|67 89 17 63 34 41 53 37 61 18 88 70 91 13 19 65 99 81 3 62|
| |
|31 32 6 11 23 94 16 73 76 7 0 75 98 27 66 28 50 9 22 38|
| |
|25 42 86 5 55 64 79 35 36 14 52 2 57 12 46 80 83 84 90 56|
| |
| 8 96 4 10 49 71 21 54 48 51 26 40 95 1 68 47 59 74 92 45|
+-----------------------------------------------------------+
|15 20 24 29-30 33 39 43-44 58 60 69 72 77-78 82 85 87 93 97|
+-----------------------------------------------------------+
|3 13 17-19 34 37 41 53 61-63 65 67 70 81 88-89 91 99 |
+-----------------------------------------------------------+
|0 6-7 9 11 16 22-23 27-28 31-32 38 50 66 73 75-76 94 98 |
+-----------------------------------------------------------+
|2 5 12 14 25 35-36 42 46 52 55-57 64 79-80 83-84 86 90 |
+-----------------------------------------------------------+
|1 4 8 10 21 26 40 45 47-49 51 54 59 68 71 74 92 95-96 |
+-----------------------------------------------------------+
Readable and elegant are in the eye of the beholder :D
That was a good exercise! It suggests the following segment of Perl:
sub g {
my ($i, #r, #s) = 0, local #_ = sort {$a<=>$b} #_;
$_ && $_[$_-1]+1 == $_[$_] || push(#r, $_[$_]),
$_<$#_ && $_[$_+1]-1 == $_[$_] || push(#s, $_[$_]) for 0..$#_;
join ' ', map {$_ == $s[$i++] ? $_ : "$_-$s[$i-1]"} #r;
}
Addendum
In plain English, this algorithm finds all items where the previous item is not one less, uses them for the lower bounds; finds all items where the next item is not one greater, uses them for the upper bounds; and combines the two lists together item-by-item.
Since J is pretty obscure, here's a short explanation of how that code works:
x /: y sorts the array x on y. ~ can make a dyadic verb into a reflexive monad, so /:~ means "sort an array on itself".
3 : '...' declares a monadic verb (J's way of saying "function taking one argument"). # means function composition, so g =: 3 : '...' # /:~ means "g is set to the function we're defining, but with its argument sorted first". "1 says that we operate on arrays, not tables or anything of higher dimensionality.
Note: y is always the name of the only argument to a monadic verb.
{. takes the first element of an array (head) and }: takes all but the last (curtail). ({.,}:)y effectively duplicates the first element of y and lops off the last element. 1+({.,}:)y adds 1 to it all, and ~: compares two arrays, true wherever they are different and false wherever they are the same, so y~:1+({.,}:)y is an array that is true in all the indices of y where an element is not equal to one more than the element that preceded it. (y~:1+({.,}:)y)#y selects all elements of y where the property stated in the previous sentence is true.
Similarly, }. takes all but the first element of an array (behead) and {: takes the last (tail), so }.y,{:y is all but the first element of y, with the last element duplicated. (}.y,{:y)-1 subtracts 1 to it all, and again ~: compares two arrays item-wise for non-equality while # picks.
,. zips the two arrays together, into an array of two-element arrays. ~. nubs a list (eliminates duplicates), and is given the "1 rank, so it operates on the inner two-element arrays rather than the top-level array. This is # composed with <, which puts each subarray into a box (otherwise J will extend each subarray again to form a 2D table).
g2 is a mess of boxing and unboxing (otherwise J will pad strings to equal length), and is pretty uninteresting.
Here's my Haskell entry:
runs lst = map showRun $ runs' lst
runs' l = reverse $ map reverse $ foldl newOrGlue [[]] l
showRun [s] = show s
showRun lst = show (head lst) ++ "-" ++ (show $ last lst)
newOrGlue [[]] e = [[e]]
newOrGlue (curr:other) e | e == (1 + (head curr)) = ((e:curr):other)
newOrGlue (curr:other) e | otherwise = [e]:(curr:other)
and a sample run:
T> runs [1,2,3,5,7,9,10,11,12,14]
["1-3","5","7","9-12","14"]
Erlang , perform also sort and unique on input and can generate programmatically reusable pair and also a string representation.
group(List) ->
[First|_] = USList = lists:usort(List),
getnext(USList, First, 0).
getnext([Head|Tail] = List, First, N) when First+N == Head ->
getnext(Tail, First, N+1);
getnext([Head|Tail] = List, First, N) ->
[ {First, First+N-1} | getnext(List, Head, 0) ];
getnext([], First, N) -> [{First, First+N-1}].
%%%%%% pretty printer
group_to_string({X,X}) -> integer_to_list(X);
group_to_string({X,Y}) -> integer_to_list(X) ++ "-" ++ integer_to_list(Y);
group_to_string(List) -> [group_to_string(X) || X <- group(List)].
Test getting programmatically reusable pairs:
shell> testing:group([34,3415,56,58,57,11,12,13,1,2,3,3,4,5]).
result> [{1,5},{11,13},{34,34},{56,58},{3415,3415}]
Test getting "pretty" string:
shell> testing:group_to_string([34,3415,56,58,57,11,12,13,1,2,3,3,4,5]).
result> ["1-5","11-13","34","56-58","3415"]
hope it helps
bye
VBA
Public Function convertListToRange(lst As String) As String
Dim splLst() As String
splLst = Split(lst, ",")
Dim x As Long
For x = 0 To UBound(splLst)
Dim groupStart As Integer
groupStart = splLst(x)
Dim groupEnd As Integer
groupEnd = groupStart
Do While (x <= UBound(splLst) - 1)
If splLst(x) - splLst(x + 1) <> -1 Then Exit Do
groupEnd = splLst(x + 1)
x = x + 1
Loop
convertListToRange = convertListToRange & IIf(groupStart = groupEnd, groupStart & ",", groupStart & "-" & groupEnd & ",")
Next x
convertListToRange = Left(convertListToRange, Len(convertListToRange) - 1)
End Function
convertListToRange("1,2,3,7,8,9,11,12,99,100,101")
Return: "1-3,7-9,11-12,99-101"

Resources