We're given two sequences of lowercase latin alphabet letters.
They're both the same length and have the same amount of given types
of letters (the first has an equal number of t's as the second and so
on). We are required to find the minimum number of swaps (by "swap" we mean changing
the order of two neighboring letters) required to
transform the first sequence into the second. We
can safely assume every two sequences CAN be transformed
into each other. We could do this with brute-force, but the sequences are too long for that.
Input:
The length of the sequences (at least 2, at most 999999) and
then two sequences.
Output:
An integer representing the number of swaps needed for the
sequences to become the same.
Example:
{5, aaaaa, aaaaa} should output {0},
{4, abcd, acdb} should output {2}.
The first thing that came to my mind was bubblesort. We can simply bubblesort the sequence counting each swap. The problem is: a) it's O(n^2) worst-case b) I'm not convinced it would give me the smallest number for every case... Even the optimized bubblesort doesn't seem to be doing the trick. We could implement the cocktail sort which would solve the problem with turtles - but will it give me the best performance? Or maybe there's something simpler/faster?
This question can also be phrased as: How can we determine the edit distance between two strings when the only operation allowed is transposition?
Regarding the minimum number of (not necessarily adjacent) swaps needed to convert a permutation into another, the metric you should use is the Cayley distance which is essentially the size of the permutation - the number of cycles.
Counting the number of cycles in a permutation is a quite trivial issue. A simple example. Suppose permutation 521634.
If you check the first position, you have 5, in the 5th you have 3 and in the 3rd you have 1, closing the first cycle. 2 is in the 2nd position, so it make a cycle itself and 4 and 6 make the last cycle (4 is in the 6th position and 6 in the 4th). If you want to convert this permutation in the identity permutation (with the minimum number of swaps), you need to reorder each cycle independently. The total number of swaps is the length of the permutation (6) minus the number of cycles (3).
Given any two permutations, the distance between them is equivalent to the distance between the composition of the first with the inverse of the second and the identity (computed as explained above). Therefore, the only thing you need to do is composing the first permutation and the inverse of the second and count the number of cycles in the result. All these operations are O(n), so you can get the minimum number of swaps in linear time.
Here's a simple and efficient solution:
Let Q[ s2[i] ] = the positions character s2[i] is on in s2. Let P[i] = on what position is the character corresponding to s1[i] in the second string.
To build Q and P:
for ( int i = 0; i < s1.size(); ++i )
Q[ s2[i] ].push_back(i); // basically, Q is a vector [0 .. 25] of lists
temp[0 .. 25] = {0}
for ( int i = 0; i < s1.size(); ++i )
P[i + 1] = 1 + Q[ s1[i] ][ temp[ s1[i] ]++ ];
Example:
1234
s1: abcd
s2: acdb
Q: Q[a = 0] = {0}, Q[b = 1] = {3}, Q[c = 2] = {1}, Q[d = 3] = {2}
P: P[1] = 1, P[2] = 4 (because the b in s1 is on position 4 in s2), P[3] = 2
P[4] = 3
P has 2 inversions (4 2 and 4 3), so this is the answer.
This solution is O(n log n) because building P and Q can be done in O(n) and merge sort can count inversions in O(n log n).
What you are looking for may be identical to the "Kendall tau distance", which is the (normalized) difference of concordant minus discordant pairs. See Wikipedia, where it is claimed that it is equivalent to the bubble sort distance.
In R, functions are avialable not only for computing tau, e.g.
cor( X, method="kendall", use="pairwise" ) ,
but also for testing the significance of the difference, e.g.
cor.test( x1, x2, method="kendall" ) ,
and they are even able to properly take into account ties.
See here for more.
"Kendall tau distance" algorithm is the exact solution in this case, where the number of swaps of adjacent elements must be found.
Example.
eyssaasse (base string)
seasysaes
Base string provides indexes for each element: e=0, y=1, s=2, s=3, a=4, a=5, s=6, s=7, e=8;
Some elements are duplicate, so:
1) Create a dictionary where elements are keys, and values are lists of indices:
idx = {'e'=>[0, 8], 'y'=>[1], 's'=>[2, 3, 6, 7], 'a'=>[4, 5]}
2) Create an index map of the second string using element indexes in the idx dictionary:
seasysaes -> 204316587 (loop 'seasysaes' and pop next index from lists for each key in idx)
3) Create a list of all paired combinations of this map, 204316587: 20 24 23 21 26 25 28 27 04 03 01 06 ... 65 68 67 58 57 87;
Loop through these pairs counting those where first number bigger than second number.
This count is the sought-for number of adjacent swaps between strings.
Python script:
from itertools import combinations, cycle
word = 'eyssaasse' # base string
cmpr = 'seasysaes' # a string to find number of swaps from the base string
swaps = 0
# 1)
chars = {c: [] for c in word}
[chars[c].append(i) for i, c in enumerate(word)]
for k in chars.keys():
chars[k] = cycle(chars[k])
# 2)
idxs = [next(chars[c]) for c in cmpr]
# 3)
for cmb in combinations(idxs, 2):
if cmb[0] > cmb[1]:
swaps += 1
print(swaps)
Number of swaps between 'eyssaasse' and 'seasysaes' is 7.
For 'reviver' and 'vrerevi' it's 8.
I have written a class Permutation which among other things can return a number of transpositions needed to convert given permutation into identity. This is done by creating orbits (cycles) and counting their lengths. Terminology is taken from Kostrikin A., I., "Introduction to Linear Algebra I".
Includes:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <iterator>
class Permutation:
class Permutation {
public:
struct ei_element { /* element of the orbit*/
int e; /* identity index */
int i; /* actual permutation index */
};
typedef std::vector<ei_element> Orbit; /* a cycle */
Permutation( std::vector<int> const& i_vector);
/* permute i element, vector is 0 indexed */
int pi( int i) const { return iv[ i - 1]; }
int i( int k) const { return pi( k); } /* i_k = pi(k) */
int q() const { /* TODO: return rank = q such that pi^q = e */ return 0; }
int n() const { return n_; }
/* return the sequence 1, 2, ..., n */
std::vector<int> const& Omega() const { return ev; }
/* return vector of cycles */
std::vector<Orbit> const& orbits() const { return orbits_; }
int l( int k) const { return orbits_[ k].size(); } /* length of k-th cycle */
int transpositionsCount() const; /* return sum of all transpositions */
void make_orbits();
private:
struct Increment {
int current;
Increment(int start) : current(start) {}
int operator() () {
return current++;
}
};
int n_;
std::vector<int> iv; /* actual permutation */
std::vector<int> ev; /* identity permutation */
std::vector<Orbit> orbits_;
};
Definitions:
Permutation::Permutation( std::vector<int> const& i_vector) :
n_( i_vector.size()),
iv( i_vector), ev( n_) {
if ( n_) { /* fill identity vector 1, 2, ..., n */
Increment g ( 1);
std::generate( ev.begin(), ev.end(), g);
}
}
/* create orbits (cycles) */
void Permutation::make_orbits() {
std::set<int> to_visit( ev.begin(), ev.end()); // identity elements to visit
while ( !to_visit.empty()) {
/* new cycle */
Orbit orbit;
int first_to_visit_e = *to_visit.begin();
to_visit.erase( first_to_visit_e);
int k = first_to_visit_e; // element in identity vector
/* first orbit element */
ei_element element;
element.e = first_to_visit_e;
element.i = i( first_to_visit_e);
orbit.push_back( element);
/* traverse permutation until cycle is closed */
while ( pi( k) != first_to_visit_e && !to_visit.empty()) {
k = pi( k);
ei_element element;
element.e = k;
element.i = pi( k);
orbit.push_back( element);
to_visit.erase( k);
}
orbits_.push_back( orbit);
}
}
and:
/* return sum of all transpositions */
int Permutation::transpositionsCount() const {
int count = 0;
int k = 0;
while ( k < orbits_.size()) {
count += l( k++) - 1; /* sum += l_k - 1 */
}
return count;
}
usage:
/*
*
*/
int main(int argc, char** argv) {
//1, 2, 3, 4, 5, 6, 7, 8 identity (e)
int permutation[] = {2, 3, 4, 5, 1, 7, 6, 8}; // actual (i)
std::vector<int> vp( permutation, permutation + 8);
Permutation p( vp);
p.make_orbits();
int k = p.orbits().size();
std::cout << "Number of cycles:" << k << std::endl;
for ( int i = 0; i < k; ++i) {
std::vector<Permutation::ei_element> v = p.orbits()[ i];
for ( int j = 0; j < v.size(); ++j) {
std::cout << v[ j].e << "," << v[ j].i << " | ";
}
std::cout << std::endl;
}
std::cout << "Steps needed to create identity permutation: "
<< p.transpositionsCount();
return 0;
}
output:
Number of cycles:3
1,2 | 2,3 | 3,4 | 4,5 | 5,1 |
6,7 | 7,6 |
8,8 |
Steps needed to create identity permutation: 5
RUN SUCCESSFUL (total time: 82ms)
coliru
Converting permutation from one to another can be converted to a similar problem (Number of swaps in a permutation) by inverting the target permutation in O(n), composing the permutations in O(n) and then finding the number of swaps from there to an identity permutation.
Given:
int P1[] = {0, 1, 2, 3}; // abcd
int P2[] = {0, 2, 3, 1}; // acdb
// we can follow a simple algebraic modification
// (see http://en.wikipedia.org/wiki/Permutation#Product_and_inverse):
// P1 * P = P2 | premultiply P1^-1 *
// P1^-1 * P1 * P = P1^-1 * P2
// I * P = P1^-1 * P2
// P = P1^-1 * P2
// where P is a permutation that makes P1 into P2.
// also, the number of steps from P to identity equals
// the number of steps from P1 to P2.
int P1_inv[4];
for(int i = 0; i < 4; ++ i)
P1_inv[P1[i]] = i;
// invert the first permutation O(n)
int P[4];
for(int i = 0; i < 4; ++ i)
P[i] = P2[P1_inv[i]];
// chain the permutations
int num_steps = NumSteps(P, 4); // will return 2
// now we just need to count the steps
To count the steps, a simple algorithm can be devised, such as:
int NumSteps(int *P, int n)
{
int count = 0;
for(int i = 0; i < n; ++ i) {
for(; P[i] != i; ++ count) // could be permuted multiple times
swap(P[P[i]], P[i]); // look where the number at hand should be
}
// count number of permutations
return count;
}
This always swaps an item for a place where it should be in the identity permutation, therefore at every step it undoes and counts one swap. Now, provided that the number of swaps it returns is indeed minimum, the runtime of the algorithm is bounded by it and is guaranteed to finish (instead of getting stuck in an infinite loop). It will run in O(m) swaps or O(m + n) loop iterations where m is number of swaps (the count returned) and n is number of items in the sequence (4). Note that m < n is always true. Therefore, this should be superior to O(n log n) solutions, as the upper bound is O(n - 1) of swaps or O(n + n - 1) of loop iterations here, which is both practically O(n) (constant factor of 2 omitted in the latter case).
The algorithm will only work for valid permutations, it will loop infinitely for sequences with duplicate values and will do out-of-bounds array access (and crash) for sequences with values other than [0, n). A complete test case can be found here (builds with Visual Studio 2008, the algorithm itself should be fairly portable). It generates all possible permutations of lengths 1 to 32 and checks against solutions, generated with breadth first search (BFS), seems to work for all of permutations of lengths 1 to 12, then it becomes fairly slow but I assume it will just continue working.
Related
Farey sequence of order n is the sequence of completely reduced fractions, between 0 and 1 which when in lowest terms have denominators less than or equal to n, arranged in order of increasing size. Detailed explanation here.
Problem
The problem is, given n and k, where n = order of seq and k = element index, can we find the particular element from the sequence. For examples answer for (n=5, k =6) is 1/2.
Lead
There are many less than optimal solution available, but am looking for a near-optimal one. One such algorithm is discussed here, for which I am unable to understand the logic hence unable to apply the examples.
Question
Can some please explain the solution with more detail, preferably with an example.
Thank you.
I've read the method provided in your link, and the accepted C++ solution to it. Let me post them, for reference:
Editorial Explanation
Several less-than-optimal solutions exist. Using a priority queue, one
can iterate through the fractions (generating them one by one) in O(K
log N) time. Using a fancier math relation, this can be reduced to
O(K). However, neither of these solution obtains many points, because
the number of fractions (and thus K) is quadratic in N.
The “good” solution is based on meta-binary search. To construct this
solution, we need the following subroutine: given a fraction A/B
(which is not necessarily irreducible), find how many fractions from
the Farey sequence are less than this fraction. Suppose we had this
subroutine; then the algorithm works as follows:
Determine a number X such that the answer is between X/N and (X+1)/N; such a number can be determined by binary searching the range
1...N, thus calling the subroutine O(log N) times.
Make a list of all fractions A/B in the range X/N...(X+1)/N. For any given B, there is at most one A in this range, and it can be
determined trivially in O(1).
Determine the appropriate order statistic in this list (doing this in O(N log N) by sorting is good enough).
It remains to show how we can construct the desired subroutine. We
will show how it can be implemented in O(N log N), thus giving a O(N
log^2 N) algorithm overall. Let us denote by C[j] the number of
irreducible fractions i/j which are less than X/N. The algorithm is
based on the following observation: C[j] = floor(X*B/N) – Sum(C[D],
where D divides j). A direct implementation, which tests whether any D
is a divisor, yields a quadratic algorithm. A better approach,
inspired by Eratosthene’s sieve, is the following: at step j, we know
C[j], and we subtract it from all multiples of j. The running time of
the subroutine becomes O(N log N).
Relevant Code
#include <cassert>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
const int kMaxN = 2e5;
typedef int int32;
typedef long long int64_x;
// #define int __int128_t
// #define int64 __int128_t
typedef long long int64;
int64 count_less(int a, int n) {
vector<int> counter(n + 1, 0);
for (int i = 2; i <= n; i += 1) {
counter[i] = min(1LL * (i - 1), 1LL * i * a / n);
}
int64 result = 0;
for (int i = 2; i <= n; i += 1) {
for (int j = 2 * i; j <= n; j += i) {
counter[j] -= counter[i];
}
result += counter[i];
}
return result;
}
int32 main() {
// ifstream cin("farey.in");
// ofstream cout("farey.out");
int64_x n, k; cin >> n >> k;
assert(1 <= n);
assert(n <= kMaxN);
assert(1 <= k);
assert(k <= count_less(n, n));
int up = 0;
for (int p = 29; p >= 0; p -= 1) {
if ((1 << p) + up > n)
continue;
if (count_less((1 << p) + up, n) < k) {
up += (1 << p);
}
}
k -= count_less(up, n);
vector<pair<int, int>> elements;
for (int i = 1; i <= n; i += 1) {
int b = i;
// find a such that up/n < a / b and a / b <= (up+1) / n
int a = 1LL * (up + 1) * b / n;
if (1LL * up * b < 1LL * a * n) {
} else {
continue;
}
if (1LL * a * n <= 1LL * (up + 1) * b) {
} else {
continue;
}
if (__gcd(a, b) != 1) {
continue;
}
elements.push_back({a, b});
}
sort(elements.begin(), elements.end(),
[](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
return 1LL * lhs.first * rhs.second < 1LL * rhs.first * lhs.second;
});
cout << (int64_x)elements[k - 1].first << ' ' << (int64_x)elements[k - 1].second << '\n';
return 0;
}
Basic Methodology
The above editorial explanation results in the following simplified version. Let me start with an example.
Let's say, we want to find 7th element of Farey Sequence with N = 5.
We start with writing a subroutine, as said in the explanation, that gives us the "k" value (how many Farey Sequence reduced fractions there exist before a given fraction - the given number may or may not be reduced)
So, take your F5 sequence:
k = 0, 0/1
k = 1, 1/5
k = 2, 1/4
k = 3, 1/3
k = 4, 2/5
k = 5, 1/2
k = 6, 3/5
k = 7, 2/3
k = 8, 3/4
k = 9, 4/5
k = 10, 1/1
If we can find a function that finds the count of the previous reduced fractions in Farey Sequence, we can do the following:
int64 k_count_2 = count_less(2, 5); // result = 4
int64 k_count_3 = count_less(3, 5); // result = 6
int64 k_count_4 = count_less(4, 5); // result = 9
This function is written in the accepted solution. It uses the exact methodology explained in the last paragraph of the editorial.
As you can see, the count_less() function generates the same k values as in our hand written list.
We know the values of the reduced fractions for k = 4, 6, 9 using that function. What about k = 7? As explained in the editorial, we will list all the reduced fractions in range X/N and (X+1)/N, here X = 3 and N = 5.
Using the function in the accepted solution (its near bottom), we list and sort the reduced fractions.
After that we will rearrange our k values, as in to fit in our new array as such:
k = -, 0/1
k = -, 1/5
k = -, 1/4
k = -, 1/3
k = -, 2/5
k = -, 1/2
k = -, 3/5 <-|
k = 0, 2/3 | We list and sort the possible reduced fractions
k = 1, 3/4 | in between these numbers
k = -, 4/5 <-|
k = -, 1/1
(That's why there is this piece of code: k -= count_less(up, n);, it basically remaps the k values)
(And we also subtract one more during indexing, i.e.: cout << (int64_x)elements[k - 1].first << ' ' << (int64_x)elements[k - 1].second << '\n';. This is just to basically call the right position in the generated array.)
So, for our new re-mapped k values, for N = 5 and k = 7 (original k), our result is 2/3.
(We select the value k = 0, in our new map)
If you compile and run the accepted solution, it will give you this:
Input: 5 7 (Enter)
Output: 2 3
I believe this is the basic point of the editorial and accepted solution.
I'm looking for a solution for this task:
There are three permuted integer lists:
index
0 1 2 3
[2,4,3,1]
[3,4,1,2]
[1,2,4,3]
I'd like to know how many combinations of three tuples across the lists there are. For example, after rotating the second list by one to the right and the third list by one to the left:
0 1 2 3
[2,4,3,1]
3 0 1 2
[2,3,4,1]
1 2 3 0
[2,4,3,1]
would result in two combinations (2,2,2) and (1,1,1). I'm only interested in the number of combinations, not the actual combinations themselves.
The lists have always the same length N. From my understanding, there are is at least one combination and maximally N.
I've written an imperative solution, using three nested for loops, but for larger problems sizes (e.g. N > 1000) this quickly becomes unbearable.
Is there are more efficient approach than brute force (trying all combinations)?. Maybe some clever algorithm or a mathematical trick?
Edit:
I'm rephrasing the question to make it (hopefully) more clear:
I have 3 permutations of a list [1..N].
The lists can be individually rotated left or right, until the elements for some indexes line up. In the above example that would be:
Right rotate list 2 by 1
Left rotate list 3 by 1
Now the columns are aligned for 2 and 1.
I've also added the indexes the example above. Please tell me, if it's still unclear.
My code so far:
#include <iostream>
int
solve(int n, int * a, int * b, int * c)
{
int max = 0;
for (int i = 0; i < n; ++i) {
int m = 0;
for (int j = 0; j < n; ++j) {
if (a[i] == b[j]) {
for (int k = 0; k < n; ++k) {
if (a[i] == c[k]) {
for (int l = 0; l < n; ++l) {
if (a[l] == b[(l+j) % n] && a[l] == b[(l+k) % n]) {
++m;
}
}
}
}
}
}
if (m > max) {
max = m;
}
}
return max;
}
int
main(int argc, char ** argv)
{
int n = 5;
int a[] = { 1, 5, 4, 3, 2 };
int b[] = { 1, 3, 2, 4, 5 };
int c[] = { 2, 1, 5, 4, 3 };
std::cout << solve(n, a, b, c) << std::endl;
return 0;
}
Here is an efficient solution:
Let's assume that we have picked a fixed element from the first list and we want to match it to the elements from the second and the third list with the same value. It uniquely determines the rotation of the second and the third list(we can assume that the first list is never rotated). It gives us a pair of two integers: (the position of this element in the first list minus its position in the second list modulo N, the same thing for the first and the third list).
Now we can iterate over all elements of the first list and generate these pairs.
The answer is the number of ocurrences of the most frequent pair.
The time complexity is O(N * log N) if we use standard sort to find the most frequent pair or O(N) if we use radix sort or a hash table.
You can make it by creating all combinations like:
0,0,0
0,0,1
0,1,0
0,1,1
1,0,0
1,0,1
1,1,0
1,1,1
Each 0 / 1 can be your array
This code can help you creating this list above:
private static ArrayList<String> getBinaryArray(ArrayList<Integer> array){
//calculating the possible combinations we can get
int possibleCombinations = (int) Math.pow(2, array.size());
//creating an array with all the possible combinations in binary
String binary = "";
ArrayList<String> binaryArray = new ArrayList<String>();
for (int k = 0; k <possibleCombinations; k++) {
binary = Integer.toBinaryString(k);
//adding '0' as much as we need
int len = (array.size() - binary.length());
for (int w = 1; w<=len; w++) {
binary = "0" + binary;
}
binaryArray.add(binary);
}
return binaryArray;
}
it's also either can be with 0/1/2 numbers which each other number can be the lists you got.
if it's not so clear please tell me
First, define two integers N and K, where N >= K, both known at compile time. For example: N = 8 and K = 3.
Next, define a set of integers [0, N) (or [1, N] if that makes the answer simpler) and call it S. For example: {0, 1, 2, 3, 4, 5, 6, 7}
The number of subsets of S with K elements is given by the formula C(N, K). Example
My problem is this: Create a perfect minimal hash for those subsets. The size of the example hash table will be C(8, 3) or 56.
I don't care about ordering, only that there be 56 entries in the hash table, and that I can determine the hash quickly from a set of K integers. I also don't care about reversibility.
Example hash: hash({5, 2, 3}) = 42. (The number 42 isn't important, at least not here)
Is there a generic algorithm for this that will work with any values of N and K? I wasn't able to find one by searching Google, or my own naive efforts.
There is an algorithm to code and decode a combination into its number in the lexicographical order of all combinations with a given fixed K. The algorithm is linear to N for both code and decode of the combination. What language are you interested in?
EDIT: here is example code in c++(it founds the lexicographical number of a combination in the sequence of all combinations of n elements as opposed to the ones with k elements but is really good starting point):
typedef long long ll;
// Returns the number in the lexicographical order of all combinations of n numbers
// of the provided combination.
ll code(vector<int> a,int n)
{
sort(a.begin(),a.end());
int cur = 0;
int m = a.size();
ll res =0;
for(int i=0;i<a.size();i++)
{
if(a[i] == cur+1)
{
res++;
cur = a[i];
continue;
}
else
{
res++;
int number_of_greater_nums = n - a[i];
for(int j = a[i]-1,increment=1;j>cur;j--,increment++)
res += 1LL << (number_of_greater_nums+increment);
cur = a[i];
}
}
return res;
}
// Takes the lexicographical code of a combination of n numbers and returns the
// combination
vector<int> decode(ll kod, int n)
{
vector<int> res;
int cur = 0;
int left = n; // Out of how many numbers are we left to choose.
while(kod)
{
ll all = 1LL << left;// how many are the total combinations
for(int i=n;i>=0;i--)
{
if(all - (1LL << (n-i+1)) +1 <= kod)
{
res.push_back(i);
left = n-i;
kod -= all - (1LL << (n-i+1)) +1;
break;
}
}
}
return res;
}
I am sorry I have an algorithm for the problem you are asking for right now, but I believe it will be a good exercise to try to understand what I do above. Truth is this is one of the algorithms I teach in the course "Design and analysis of algorithms" and that is why I had it pre-written.
This is what you (and I) need:
hash() maps k-tuples from [1..n] onto the set 1..C(n,k)\subset N.
The effort is k subtractions (and O(k) is a lower bound anyway, see Strandjev's remark above):
// bino[n][k] is (n "over" k) = C(n,k) = {n \choose k}
// these are assumed to be precomputed globals
int hash(V a,int n, int k) {// V is assumed to be ordered, a_k<...<a_1
// hash(a_k,..,a_2,a_1) = (n k) - sum_(i=1)^k (n-a_i i)
// ii is "inverse i", runs from left to right
int res = bino[n][k];
int i;
for(unsigned int ii = 0; ii < a.size(); ++ii) {
i = a.size() - ii;
res = res - bino[n-a[ii]][i];
}
return res;
}
A taxicab number is an integer that can be expressed as the sum of two cubes of integers in two different ways: a^3+b^3 = c^3+d^3. Design an algorithm to find all taxicab numbers with a, b, c, and d less than N.
Please give both the space and time complexity in terms of N.
I could do it in o(N^2.logN) time with O(N^2) space.
Best algorithm I've found so far:
Form all pairs: N^2
Sort the sum: N^2 logN
Find duplicates less than N
But this takes N^2 space. Can we do better?
But this takes N^2 space. Can we do better?
There exists an O(N) space solution based on a priority queue. Time complexity is O(N^2 logN). To sketch out the idea of the algorithm, here is the matrix M such that M[i][j] = i^3 + j^3 (of course, the matrix is never created in memory):
0 1 8 27 64 125
1 2 9 28 65 126
8 9 16 35 72 133
27 28 35 54 91 152
64 65 72 91 128 189
125 126 133 152 189 250
Observe that every line and every row is sorted in ascending order. Let PQ be the priority queue. First we put the biggest element in the priority queue. Then perform the following, as long as the PQ is not empty:
Pop the biggest element from PQ
add adjacent element above if the PQ doesn't have any element from that row
add adjacent element on the left if the PQ doesn't have any element from that column, and if it is not under the diagonal of the matrix (to avoid redundant elements)
Note that
You don't need to create the matrix in memory to implement the algorithm
The elements will be popped from the PQ in descending order, from the biggest element of the matrix to its smallest one (avoiding elements from the redundant half part of the matrix).
Everytime the PQ issues the same value twice then we have found a taxicab number.
As an illustration, here is an implementation in C++. The time complexity is O(N^2 logN) and space complexity O(N).
#include <iostream>
#include <cassert>
#include <queue>
using namespace std;
typedef unsigned int value_type;
struct Square
{
value_type i;
value_type j;
value_type sum_of_cubes;
Square(value_type i, value_type j) : i(i), j(j), sum_of_cubes(i*i*i+j*j*j) {}
friend class SquareCompare;
bool taxicab(const Square& sq) const
{
return sum_of_cubes == sq.sum_of_cubes && i != sq.i && i != sq.j;
}
friend ostream& operator<<(ostream& os, const Square& sq);
};
class SquareCompare
{
public:
bool operator()(const Square& a, const Square& b)
{
return a.sum_of_cubes < b.sum_of_cubes;
}
};
ostream& operator<<(ostream& os, const Square& sq)
{
return os << sq.i << "^3 + " << sq.j << "^3 = " << sq.sum_of_cubes;
}
int main()
{
const value_type N=2001;
value_type count = 0;
bool in_i [N];
bool in_j [N];
for (value_type i=0; i<N; i++) {
in_i[i] = false;
in_j[i] = false;
}
priority_queue<Square, vector<Square>, SquareCompare> p_queue;
p_queue.push(Square(N-1, N-1));
in_i[N-1] = true;
in_j[N-1] = true;
while(!p_queue.empty()) {
Square sq = p_queue.top();
p_queue.pop();
in_i[sq.i] = false;
in_j[sq.j] = false;
// cout << "pop " << sq.i << " " << sq.j << endl;
if (sq.i > 0 && !in_i[sq.i - 1] && sq.i-1 >= sq.j) {
p_queue.push(Square(sq.i-1, sq.j));
in_i[sq.i-1] = true;
in_j[sq.j] = true;
// cout << "push " << sq.i-1 << " " << sq.j << endl;
}
if (sq.j > 0 && !in_j[sq.j-1] && sq.i >= sq.j - 1) {
p_queue.push(Square(sq.i, sq.j-1));
in_i[sq.i] = true;
in_j[sq.j - 1] = true;
// cout << "push " << sq.i << " " << sq.j-1 << endl;
}
if (sq.taxicab(p_queue.top())) {
/* taxicab number */
cout << sq << " " << p_queue.top() << endl;
count++;
}
}
cout << endl;
cout << "there are " << count << " taxicab numbers with a, b, c, d < " << N << endl;
return 0;
}
The answers given by Novneet Nov and user3017842 are both correct ideas for finding the taxicab numbers with storage O(N) using minHeap.
Just a little bit more explanation why the minHeap of size N works.
First, if you had all the sums (O(N^2)) and could sort them (O(N^2lgN)) you would just pick the duplicates as you traverse the sorted array. Well, in our case using a minHeap we can traverse in-order all the sums: we just need to ensure that the minHeap always contains the minimum unprocessed sum.
Now, we have a huge number of sums (O(N^2)). But, notice that this number can be split into N groups each of which has an easily defined minimum!
(fix a, change b from 0 to N-1 => here are your N groups. The sum in one group with a smaller b is smaller than one with a bigger b in the same group - because a is the same).
The minimum of union of these groups is in the union of mins of these
groups. Therefore, if you keep all minimums of these groups in the
minHeap you are guaranteed to have the total minimum in the minHeap.
Now, when you extract Min from the heap, you just add next smallest element from the group of this extracted min (so if you extracted (a, b) you add (a, b+1)) and you are guaranteed that your minHeap still contains the next unprocessed min of all the sums.
I found the solution/code here : Time complexity O(N^2 logN), space complexity O(N)
The solution is implemented by help of priority queues.
Reverse thinking can be easily done by looking at the code. It can be done in an array of size N because the min sums are deleted from the array after comparing to the next minimum and then the array is made to size N by adding a new sum - (i^3 + (j+1)^3).
A intuitive proof is here :
Initially, we have added (1,1),(2,2),(3,3),...,(N,N) in the min-priority queue.
Suppose a^+b^3=c^3+d^3, and (a,b) is the minimum that will be taken out of the priority queue next. To be able to detect this taxicab number, (c,d) must also be in the priority queue which would be taken out after (a,b).
Note: We would be adding (a,b+1) after extracting (a,b) so there is no way that extraction of (a,b) would result in addition of (c,d) to the priority queue, so it must already exist in the priority queue.
Now lets assume that (c,d) is not in the priority queue, because we haven't gotten to it yet. Instead, there is some (c,d−k) in the priority queue where k>0.
Since (a,b) is being taken out,
a^3+b^3≤c^3+(d−k)^3
However, a^3+b^3=c^3+d^3
Therefore,
c^3+d^3≤c^3+(d−k)^3
d≤d−k
k≤0
Since k>0, this is impossible. Thus our assumption can never come to pass.
Thus for every (a,b) which is being removed from the min-PQ, (c,d) is already in the min-PQ (or was just removed) if a^3+b^3=c^3+d^3
The time complexity of the algorithm can't be less than O(N2) in any case, since you might print up to O(N2) taxicab numbers.
To reduce space usage you could, in theory, use the suggestion mentioned here: little link. Basically, the idea is that first you try all possible pairs a, b and find the solution to this:
a = 1 − (p − 3 * q)(p2 + 3 * q2)
b = −1 + (p + 3 * q)(p2 + 3q2)
Then you can find the appropriate c, d pair using:
c = (p + 3 * q) - (p2 + 3 * q2)
d = -(p - 3 * q) + (p2 + 3 * q2)
and check whether they are both less than N. The issue here is that solving that system of equations might get a bit messy (by 'a bit' I mean very tedious).
The O(N2) space solution is much simpler, and it'd probably be efficient enough since anything of quadratic time complexity that can run in reasonable time limits will probably be fine with quadratic space usage.
I hope that helped!
version1 uses List and sorting
O(n^2*logn) time and O(n^2) space
public static void Taxicab1(int n)
{
// O(n^2) time and O(n^2) space
var list = new List<int>();
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
list.Add(i * i * i + j * j * j);
}
}
// O(n^2*log(n^2)) time
list.Sort();
// O(n^2) time
int prev = -1;
foreach (var next in list)
{
if (prev == next)
{
Console.WriteLine(prev);
}
prev = next;
}
}
version2 uses HashSet
O(n^2) time and O(n^2) space
public static void Taxicab2(int n)
{
// O(n^2) time and O(n^2) space
var set = new HashSet<int>();
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
int x = i * i * i + j * j * j;
if (!set.Add(x))
{
Console.WriteLine(x);
}
}
}
}
version3 uses min oriented Priority Queue
O(n^2*logn) time and O(n) space
public static void Taxicab3(int n)
{
// O(n) time and O(n) space
var pq = new MinPQ<SumOfCubes>();
for (int i = 1; i <= n; i++)
{
pq.Push(new SumOfCubes(i, i));
}
// O(n^2*logn) time
var sentinel = new SumOfCubes(0, 0);
while (pq.Count > 0)
{
var current = pq.Pop();
if (current.Result == sentinel.Result)
Console.WriteLine($"{sentinel.A}^3+{sentinel.B}^3 = {current.A}^3+{current.B}^3 = {current.Result}");
if (current.B <= n)
pq.Push(new SumOfCubes(current.A, current.B + 1));
sentinel = current;
}
}
where SummOfCubes
public class SumOfCubes : IComparable<SumOfCubes>
{
public int A { get; private set; }
public int B { get; private set; }
public int Result { get; private set; }
public SumOfCubes(int a, int b)
{
A = a;
B = b;
Result = a * a * a + b * b * b;
}
public int CompareTo(SumOfCubes other)
{
return Result.CompareTo(other.Result);
}
}
github
create an array: 1^3, 2^3, 3^3, 4^3, ....... k^3. such that k^3 < N and (k+1)^3 > N. the array size would be ~ (N)^(1/3). the array is sorted order.
use 2sum technique (link) in lineal time proportional to the array size. if we find 2 pairs of numbers, that is a hit.
looping through step 2 by decreasing N by 1 each time.
This will use O(N^(1/3)) extra space and ~ O(N^(4/3)) time.
A easy way of understanding Time complexity O(N^2 logN), space complexity O(N) is to think it as a merge of N sorted arrays plus a bookkeeping of the previously merged element.
It seems like a simple brute-force algorithm with proper bounds solves it in time proportional to n^1.33 and space proportional to n. Or could anyone point me to the place where I'm mistaken?
Consider 4 nested loops, each running from 1 to cubic root of n. Using these loops we can go over all possible combinations of 4 values and find the pairs forming taxicab numbers. It means each loop takes time proportional to cubic root of n, or n^(1/3). Multiply this value 4 times and get:
(n^(1/3)^4 = n^(4/3) = n^1.33
I wrote a solution in JavaScript and benchmarked it, and it seems to be working. One caveat is that the result is only partially sorted.
Here is my JavaScript code (it's not optimal yet, could be optimized even more):
function taxicab(n) {
let a = 1, b = 1, c = 1, d = 1,
cubeA = a**3 + b**3,
cubeB = c**3 + d**3,
results = [];
while (cubeA < n) { // loop over a
while (cubeA < n) { // loop over b
// avoid running nested loops if this number is already in results
if (results.indexOf(cubeA) === -1) {
while (cubeB <= cubeA) { // loop over c
while (cubeB <= cubeA) { // loop over d
if (cubeB === cubeA && a!=c && a!=d) { // found a taxicab number!
results.push(cubeA);
}
d++;
cubeB = c**3 + d**3;
} // end loop over d
c++;
d = c;
cubeB = c**3 + d**3;
} // end loop over c
}
b++;
cubeA = a**3 + b**3;
c = d = 1;
cubeB = c**3 + d**3;
} // end loop over d
a++;
b = a;
cubeA = a**3 + b**3;
} // end loop over a
return results;
}
Running taxicab(1E8) takes around 30 seconds in a browser console and yields 485 numbers as a result. Ten times smaller value taxicab(1E7) (10 millions) takes almost 1.4 seconds and yields 150 numbers. 10^1.33 * 1.4 = 29.9, i.e. multiplying n by 10 leads to the running time increased by 10^1.33 times. The result array is unsorted, but after quickly sorting it we get correct result, as it seems:
[1729, 4104, 13832, 20683, 32832, 39312, 40033, 46683, 64232, 65728,
110656, 110808, 134379, 149389, 165464, 171288, 195841, 216027, 216125,
262656, 314496, 320264, 327763, 373464, 402597, 439101, 443889, 513000,
513856, 515375, 525824, 558441, 593047, 684019, 704977, 805688, 842751,
885248, 886464, 920673, 955016, 984067, 994688, 1009736, 1016496, 1061424,
1073375, 1075032, 1080891, 1092728, 1195112, 1260441, 1323712, 1331064,
1370304, 1407672, 1533357, 1566728, 1609272, 1728216, 1729000, 1734264,
1774656, 1845649, 2048391, 2101248, 2301299, 2418271, 2515968, 2562112,
2585375, 2622104, 2691451, 2864288, 2987712, 2991816, 3220776, 3242197,
3375001, 3375008, 3511872, 3512808, 3551112, 3587409, 3628233, 3798613,
3813992, 4033503, 4104000, 4110848, 4123000, 4174281, 4206592, 4342914,
4467528, 4505949, 4511808, 4607064, 4624776, 4673088, …]
Here is a code for benchmarking:
// run taxicab(n) for k trials and return the average running time
function benchmark(n, k) {
let t = 0;
k = k || 1; // how many times to repeat the trial to get an averaged result
for(let i = 0; i < k; i++) {
let t1 = new Date();
taxicab(n);
let t2 = new Date();
t += t2 - t1;
}
return Math.round(t/k);
}
Finally, I tested it:
let T = benchmark(1E7, 3); // 1376 - running time for n = 10 million
let T2 = benchmark(2E7, 3);// 4821 - running time for n = 20 million
let powerLaw = Math.log2(T2/T); // 1.3206693816701993
So it means time is proportional to n^1.32 in this test. Repeating this many times with different values always yields around the same result: from 1.3 to 1.4.
First of all, we will construct the taxicab numbers instead of searching for them. The range we will use to construct a taxicab number i.e Ta(2) will go up to n^1/3 not n. Because if you cube a number bigger than n^1/3 it will be bigger than n and also we can't cube negative numbers to prevent that case by definition. We will use a HashSet to remember the sums of two cubed numbers in the algorithm. This will help us to lookup previous cubed sums in O(1) time while we are iterating over every possible pair of numbers in the range I mentioned earlier.
Time complexity: O(n^2/3)
Space complexity: O(n^1/3)
def taxicab_numbers(n: int) -> list[int]:
taxicab_numbers = []
max_num = math.floor(n ** (1. / 3.))
seen_sums = set()
for i in range(1, max_num + 1):
for j in range(i, max_num + 1):
cube_sum = i ** 3 + j ** 3
if cube_sum in seen_sums:
taxicab_numbers.append(cube_sum)
else:
seen_sums.add(cube_sum)
return taxicab_numbers
import java.util.*;
public class A5Q24 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter number:");
int n = sc.nextInt();
// start checking every int less than the input
for (int a = 2;a <= n;a++) {
int count = 0;
// number of ways that number be expressed in sum of two number cubes
for (int i = 1; Math.pow(i, 3) < a; i++) {
// if the cube of number smaller is greater than the number than it goes out
for (int j = 1; j <= i; j++) {
if (Math.pow(i, 3) + Math.pow(j, 3) == a)
count++;
}
}
if (count == 2)
System.out.println(a);
}
sc.close();
}
}
I think we can also do better on time (O (N ^ 2)) with O(N ^ 2) memory, using a hashmap to check if a pair of cubes has already be seen. In Python:
def find_taxicab_numbers(n: int) -> List[Tuple[int, int, int, int, int]]:
"""
find all taxicab numbers smaller than n, i.e. integers that can be expressed as the sum of two cubes of positive
integers in two different ways so that a^3 + b^3 = c^3 + d^3.
Time: O(n ^ 2) (two loops, one dict lookup). Space: O(n ^ 2)) (all possible cubes)
:param n: upper bound for a, b, c, d
:return: list of tuples of int: a, b, c, d, and taxicab numbers
"""
cubes = [i ** 3 for i in range(n)]
seen_sum_cubes = dict() # mapping sum cubes -> a, b
taxicabs = list() # list of a, b, c, d, taxicab
# check all possible sums of cubes
for i in range(n):
for j in range(i):
sum_cubes = cubes[i] + cubes[j]
if sum_cubes in seen_sum_cubes:
prev_i, prev_j = seen_sum_cubes[sum_cubes]
taxicabs.append((i, j, prev_i, prev_j, sum_cubes))
else:
seen_sum_cubes[sum_cubes] = (i, j)
return taxicabs
Given a number N and an array of integers (all nos less than 2^15). (A is size of array 100000)
Find Maximum XOR value of N and a integer from the array.
Q is no of queries (50000) and start, stop is the range in the array.
Input:
A Q
a1 a2 a3 ...
N start stop
Output:
Maximum XOR value of N and an integer in the array with the range specified.
Eg: Input
15 2 (2 is no of queries)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
10 6 10 (Query 1)
10 6 10 (Query 2)
Output:
13
13
Code:
for(int i=start-1;i<stop;i++){
int t =no[i]^a;
if(maxxor<t)
maxxor=t;
}
cout << maxxor <<endl;
I need a algorithm 10-100 times faster than this. Sorting is too expensive. I have also tried binary trees,bit manipulation.
How about a 2x - 3x improvement?. Is that possible by optimization.
It is possible to develop faster algorithm.
Let's call bits of N: a[0], a[1], ..., a[15], e.g if N = 13 = 0000000 00001101 (in binary), then a[0] = a[1] = ... a[11] = 0, a[12] = 1, a[13] = 1, a[14] = 0, a[15] = 1.
The main idea of algorithm is following: If a[0] == 1, then best possible answer has this bit zeroed. If a[0] == 0, then best possible answer has one at this position.
So at first you check if you have some number with the desired bit. If yes, you should take only number with this bit. If no, you take it's inverse.
Then you process other bits in same manner. E.g. if a[0] == 1, a[1] == 0, you first check whether there is number beginning with zero, if yes then you check whether there is a number beginning with 01. If nothing begins with zero, then you check whether there is a number beggining with 11. And so on...
So you need a fast algorithm to answer following query: Is there a number beginning with bits ... in range start, stop?
One possibility: Constuct trie from binary representation of numbers. In each node store all positions where this prefix is in array (and sort them). Then answering to this query can be a simple walk through this trie. To check whether there is suitable prefix in start, stop range you should do a binary search over stored array in a node.
This could lead to algorithm with complexity O(lg^2 N) which is faster.
Here is the code, it hasn't been tested much, may contain bugs:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class TrieNode {
public:
TrieNode* next[2];
vector<int> positions;
TrieNode() {
next[0] = next[1] = NULL;
}
bool HasNumberInRange(int start, int stop) {
vector<int>::iterator it = lower_bound(
positions.begin(), positions.end(), start);
if (it == positions.end()) return false;
return *it < stop;
}
};
void AddNumberToTrie(int number, int index, TrieNode* base) {
TrieNode* cur = base;
// Go through all binary digits from most significant
for (int i = 14; i >= 0; i--) {
int digit = 0;
if ((number & (1 << i)) != 0) digit = 1;
cur->positions.push_back(index);
if (cur->next[digit] == NULL) {
cur->next[digit] = new TrieNode;
}
cur = cur->next[digit];
}
cur->positions.push_back(index);
}
int FindBestNumber(int a, int start, int stop, TrieNode* base) {
int best_num = 0;
TrieNode* cur = base;
for (int i = 14; i >= 0; i--) {
int digit = 1;
if ((a & (1 << i)) != 0) digit = 0;
if (cur->next[digit] == NULL ||
!cur->next[digit]->HasNumberInRange(start, stop))
digit = 1 - digit;
best_num *= 2;
best_num += digit;
cur = cur->next[digit];
}
return best_num;
}
int main() {
int n; scanf("%d", &n);
int q; scanf("%d", &q);
TrieNode base;
for (int i = 0; i < n; i++) {
int x; scanf("%d", &x);
AddNumberToTrie(x, i, &base);
}
for (int i = 0; i < q; i++) {
int a, start, stop;
// Finds biggest i, such that start <= i < stop and XOR with a is as big as possible
// Base index is 0
scanf("%d %d %d", &a, &start, &stop);
printf("%d\n", FindBestNumber(a, start, stop, &base)^a);
}
}
Your algorithm runs in linear time (O(start-stop), or O(N) for the full range). If you can't assume that the input array already has a special ordering, you probably won't be able to get it any faster.
You only can try to optimize the overhead within the loop, but that surely won't give you a significant increase in speed.
edit:
As it seems you have to search the same list multiple time, but with different start- and end indexes.
That means that pre-sorting the array is also out of the question, because that would change the order of the elements. start and end would be meaningless.
What you could try to do is avoid processing the same range twice if one query fully contains an already scanned range.
Or maybe trying to consider all queries simultaneously while iterating throug the array.
If you have multiple queries with the same range, you can build a tree with the numbers in that range like this:
Use a binary tree of depth 15 where the numbers are at the leaves and a number corresponds to the path that leads to it (left is 0 and right is 1).
e.g. for 0 1 4 7:
/ \
/ /\
/ \ / \
0 1 4 7
Then is your query is N=n_1 n_2 n_3 … n_15 where n_1 is the first bit of N, n_2 the second …
Go from the root to a leaf and when you have to make a choice if n_i = 0 (where i is the depth of the current node) then go to the right, else go to the left. When you are on the leaf, it is the max leaf.
Original Answer for one query:
Your algorithm is optimal, you need to check all numbers in the array.
There may be a way to have a slightly faster program by using programming tricks, but it has no link with the algorithm.
I just come up with a solution that requires O(AlogM) time and space for preprocessing. And O(log2M) time for each query. M is the range of the integers, 2^15 in this problem.
For the
1st..Nth number, (Tree Group 1)
1st..(A/2)th number, (A/2)th..Ath number, (Tree Group 2)
1st..(A/4)th number, (A/4)th..(A/2)th number, (A/2)th..(3A/4)th, (3A/3)th..Ath, (Tree Group 3)
......., (Tree Group 4)
.......,
......., (Tree Group logA)
construct a binary trie of the binary representation of all number in the range. There would be 2M trees. But all trees aggregated will have no more than O(AlogM) elements. For a tree that include x numbers, there can be at most logM*x node in the tree. And each number is included in only one tree in each Tree Group.
For each query, you can split the range into several ranges (no more than 2logA) that we have processed into a tree. And for each tree, we can find the maximum XOR value in O(logM) time (will explain later). That is O(logA*logM) time.
How to find the maximum in a tree? Simply prefer the 1 child if the current digit is 0 in N, otherwise prefer the 0 child. If the preferred child exist, continue to that child, otherwise to the other.
yea or you could just calculate it and not waste time thinking about how to do it better.
int maxXor(int l, int r) {
int highest_xor = 0;
int base = l;
int tbase = l;
int val = 0;
int variance = 0;
do
{
while(tbase + variance <= r)
{
val = base ^ tbase + variance;
if(val > highest_xor)
{
highest_xor = val;
}
variance += 1;
}
base +=1;
variance = 0;
}while(base <= r);
return highest_xor;
}