Jamshed11's blog

By Jamshed11, history, 18 months ago, In English

Recently I had to create large Test Cases for trees so I came up with an idea that involved the use of PBDS / Ordered Set.

So I created 2 pbds. One pbds for storing nodes I have included in the tree and the other for storing the remaining nodes. I will take 1 random value u from the first pbds and another random value v from the second pbds and then print u and v (i.e add edge between u and v). Then erase v from second pbds and insert it in first pbds cause now v is also included in the tree. I used pbds as it allows me to take any random value from it and also erasing it in log n time.

#define ll long long int

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
 
#define pbds tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>

void treeTCgenerator()
{
    ll numberOfNodes = 1e5;

    srand(time(0));

    cout << numberOfNodes << "\n";

    pbds includedInTree;
    pbds notIncludedInTree;

    ll root = 1;//almost centroid

    //uncomment the below line to create random root
    //root = ((ll) rand() * rand()) % n + 1;

    includedInTree.insert(root);

    for (ll i = 1; i <= numberOfNodes; i++)
    {
        if(i != root){
            notIncludedInTree.insert(i);
        }
    }
    
    for (ll i = 0; i < numberOfNodes - 1; i++)
    {
        ll r = rand();
        ll incSize = includedInTree.size();

        r %= incSize;

        auto itU = includedInTree.find_by_order(r);
        ll u = *itU;

        r = rand();
        
        ll notIncSize = notIncludedInTree.size();

        r %= notIncSize;

        auto itV = notIncludedInTree.find_by_order(r);
        ll v = *itV;

        notIncludedInTree.erase(itV);
        includedInTree.insert(v);

        cout << u << " " << v << "\n";
    }
}

Time Complexity: n * log n

The first value you insert in the first pbds will be close to the centroid of the tree.

Using srand(time(0)) is important otherwise it will create the same tree again and again. If you want to create a test case that involves multiple test cases then call srand(time(0)) function in your main function and remove it from treeTCgenerator function. Cause if we don't remove it then it will get called for each test case. And calling srand(time(0)) again and again also causes the problem of getting repeated random values from rand() function.

Here I have used cout for printing values. You can directly print these values in a file also.

If there are better methods for creating TC for trees please let me know.

  • Vote: I like it
  • +9
  • Vote: I do not like it

»
18 months ago, # |
  Vote: I like it +5 Vote: I do not like it
»
18 months ago, # |
  Vote: I like it +13 Vote: I do not like it

You don't really need to do that. For every $$$1 < i \leq n$$$, just print the edge ($$$u$$$, $$$i$$$), $$$u$$$ is a random number between $$$1$$$ and $$$i - 1$$$. This way, it is guaranteed that you created a random tree. This is literally a one-liner.

Code:

for(long i = 2; i <= n; ++i) cout << (rand() % (i - 1) + 1) << " " << i << "\n";
  • »
    »
    18 months ago, # ^ |
      Vote: I like it +4 Vote: I do not like it

    Thanks !!

    Lol, I never expected it was possible in just 1 line.

  • »
    »
    18 months ago, # ^ |
    Rev. 2   Vote: I like it +5 Vote: I do not like it

    I think it might be a good idea to first generate some path of length k, and then add vertices to this path. This our tree will always have a depth of atleast k. Notice that depth of most randomly generated trees will be rather small compared to amount of vertices.

    • »
      »
      »
      18 months ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      Yeah I do agree with you. The implementation is not that much harder, you just add 1 more line. But the OP didn't said any requirement about tree depth, so I just went with the barebone way of generating a tree, which has low depth and almost always has 1 as the centroid. Obviously if you are trying to make test for more advanced tree problem, it is way more than just generating a tree of depth at least k.

  • »
    »
    18 months ago, # ^ |
      Vote: I like it +8 Vote: I do not like it

    yes you do get a random tree, it isnt a particularly good one however (neither is this blog's one either i think)

    well first of all, you should really permute the vertices

    secondly, it cant be a uniform distribution of all possible trees.

    Thirdly, i think its diamater is O(log) while a general random tree has O(sqrt) diameter

    Using prufer sequence leads to better randomly generated trees

    • »
      »
      »
      18 months ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      In my case the solution of the problem was possible in just 1 dfs.

      So I just needed to confirm that N^2 will give TLE.

      So this tree worked for me.

»
18 months ago, # |
  Vote: I like it +10 Vote: I do not like it

I use this code described by Errichto in this timestamped video (~6 min). It's good for generating random trees.

»
18 months ago, # |
  Vote: I like it +19 Vote: I do not like it

If you want to generate testcases for some programming problem, I wouldn't suggest using this or any purely random tree because they'll be of $$$O(log(n))$$$ height. Instead you should find some better way of generating trees, or use a testcase generation library. In my case for example I use jngen